diff --git a/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
new file mode 100644
index 000000000..ecc843272
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
@@ -0,0 +1,55 @@
+# 0.9.0 (Unreleased)
+
+* logrus/text_formatter: don't emit empty msg
+* logrus/hooks/airbrake: move out of main repository
+* logrus/hooks/sentry: move out of main repository
+* logrus/hooks/papertrail: move out of main repository
+* logrus/hooks/bugsnag: move out of main repository
+
+# 0.8.7
+
+* logrus/core: fix possible race (#216)
+* logrus/doc: small typo fixes and doc improvements
+
+
+# 0.8.6
+
+* hooks/raven: allow passing an initialized client
+
+# 0.8.5
+
+* logrus/core: revert #208
+
+# 0.8.4
+
+* formatter/text: fix data race (#218)
+
+# 0.8.3
+
+* logrus/core: fix entry log level (#208)
+* logrus/core: improve performance of text formatter by 40%
+* logrus/core: expose `LevelHooks` type
+* logrus/core: add support for DragonflyBSD and NetBSD
+* formatter/text: print structs more verbosely
+
+# 0.8.2
+
+* logrus: fix more Fatal family functions
+
+# 0.8.1
+
+* logrus: fix not exiting on `Fatalf` and `Fatalln`
+
+# 0.8.0
+
+* logrus: defaults to stderr instead of stdout
+* hooks/sentry: add special field for `*http.Request`
+* formatter/text: ignore Windows for colors
+
+# 0.7.3
+
+* formatter/\*: allow configuration of timestamp layout
+
+# 0.7.2
+
+* formatter/text: Add configuration option for time format (#158)
diff --git a/vendor/github.com/Sirupsen/logrus/LICENSE b/vendor/github.com/Sirupsen/logrus/LICENSE
new file mode 100644
index 000000000..f090cb42f
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/LICENSE
@@ -0,0 +1,21 @@
+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.
diff --git a/vendor/github.com/Sirupsen/logrus/README.md b/vendor/github.com/Sirupsen/logrus/README.md
new file mode 100644
index 000000000..ddfe7959d
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/README.md
@@ -0,0 +1,365 @@
+# Logrus
[](https://travis-ci.org/Sirupsen/logrus) [][godoc]
+
+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). Logrus itself is completely stable and has been used in
+many large deployments. The core API is unlikely to 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(&log.TextFormatter{})` when a TTY is not
+attached, the output is compatible with the
+[logfmt](http://godoc.org/github.com/kr/logfmt) format:
+
+```text
+time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
+time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
+time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
+time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
+time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
+exit status 1
+```
+
+#### 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"
+)
+
+func init() {
+ // Log as JSON instead of the default ASCII formatter.
+ log.SetFormatter(&log.JSONFormatter{})
+
+ // 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!")
+
+ // A common pattern is to re-use fields between logging statements by re-using
+ // the logrus.Entry returned from WithFields()
+ contextLogger := log.WithFields(log.Fields{
+ "common": "this is a common field",
+ "other": "I also should be logged always",
+ })
+
+ contextLogger.Info("I'll be logged with common and other field")
+ contextLogger.Info("Me too")
+}
+```
+
+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.
+
+Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
+`init`:
+
+```go
+import (
+ log "github.com/Sirupsen/logrus"
+ "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
+ "log/syslog"
+)
+
+func init() {
+
+ // 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(airbrake.NewHook(123, "xyz", "production"))
+
+ 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)
+ }
+}
+```
+Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
+
+| Hook | Description |
+| ----- | ----------- |
+| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
+| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
+| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
+| [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. |
+| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
+| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
+| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
+| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
+| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
+| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
+| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
+| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
+| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
+| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
+| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
+| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
+| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
+| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
+| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
+| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
+| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
+
+#### 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(&log.JSONFormatter{})
+ } else {
+ // The TextFormatter is default, you don't actually have to do this.
+ log.SetFormatter(&log.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.
+* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
+
+ ```go
+ logrus.SetFormatter(&logstash.LogstashFormatter{Type: “application_name"})
+ ```
+
+Third party logging formatters:
+
+* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
+* [`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 *MyJSONFormatter) 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
+}
+```
+
+#### Logger as an `io.Writer`
+
+Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
+
+```go
+w := logger.Writer()
+defer w.Close()
+
+srv := http.Server{
+ // create a stdlib log.Logger that writes to
+ // logrus.Logger.
+ ErrorLog: log.New(w, "", 0),
+}
+```
+
+Each line written to that writer will be printed the usual way, using formatters
+and hooks. The level for those entries is `info`.
+
+#### Rotation
+
+Log rotation is not provided with Logrus. Log rotation should be done by an
+external program (like `logrotate(8)`) that can compress and delete old log
+entries. It should not be a feature of the application-level logger.
+
+#### Tools
+
+| Tool | Description |
+| ---- | ----------- |
+|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
+
+[godoc]: https://godoc.org/github.com/Sirupsen/logrus
diff --git a/vendor/github.com/Sirupsen/logrus/doc.go b/vendor/github.com/Sirupsen/logrus/doc.go
new file mode 100644
index 000000000..dddd5f877
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/doc.go
@@ -0,0 +1,26 @@
+/*
+Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
+
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+ package main
+
+ import (
+ log "github.com/Sirupsen/logrus"
+ )
+
+ func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ "number": 1,
+ "size": 10,
+ }).Info("A walrus appears")
+ }
+
+Output:
+ time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
+
+For a full guide visit https://github.com/Sirupsen/logrus
+*/
+package logrus
diff --git a/vendor/github.com/Sirupsen/logrus/entry.go b/vendor/github.com/Sirupsen/logrus/entry.go
new file mode 100644
index 000000000..9ae900bc5
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/entry.go
@@ -0,0 +1,264 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "time"
+)
+
+// Defines the key when adding errors using WithError.
+var ErrorKey = "error"
+
+// 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 an error as single field (using the key defined in ErrorKey) to the Entry.
+func (entry *Entry) WithError(err error) *Entry {
+ return entry.WithField(ErrorKey, 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}
+}
+
+// This function is not declared with a pointer value because otherwise
+// race conditions will occur when using multiple goroutines
+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) Warning(args ...interface{}) {
+ entry.Warn(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...))
+ }
+ os.Exit(1)
+}
+
+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...))
+ }
+ os.Exit(1)
+}
+
+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]
+}
diff --git a/vendor/github.com/Sirupsen/logrus/entry_test.go b/vendor/github.com/Sirupsen/logrus/entry_test.go
new file mode 100644
index 000000000..99c3b41d5
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/entry_test.go
@@ -0,0 +1,77 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEntryWithError(t *testing.T) {
+
+ assert := assert.New(t)
+
+ defer func() {
+ ErrorKey = "error"
+ }()
+
+ err := fmt.Errorf("kaboom at layer %d", 4711)
+
+ assert.Equal(err, WithError(err).Data["error"])
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+
+ assert.Equal(err, entry.WithError(err).Data["error"])
+
+ ErrorKey = "err"
+
+ assert.Equal(err, entry.WithError(err).Data["err"])
+
+}
+
+func TestEntryPanicln(t *testing.T) {
+ errBoom := fmt.Errorf("boom time")
+
+ defer func() {
+ p := recover()
+ assert.NotNil(t, p)
+
+ switch pVal := p.(type) {
+ case *Entry:
+ assert.Equal(t, "kaboom", pVal.Message)
+ assert.Equal(t, errBoom, pVal.Data["err"])
+ default:
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
+ }
+ }()
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+ entry.WithField("err", errBoom).Panicln("kaboom")
+}
+
+func TestEntryPanicf(t *testing.T) {
+ errBoom := fmt.Errorf("boom again")
+
+ defer func() {
+ p := recover()
+ assert.NotNil(t, p)
+
+ switch pVal := p.(type) {
+ case *Entry:
+ assert.Equal(t, "kaboom true", pVal.Message)
+ assert.Equal(t, errBoom, pVal.Data["err"])
+ default:
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
+ }
+ }()
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+ entry.WithField("err", errBoom).Panicf("kaboom %v", true)
+}
diff --git a/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go b/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
new file mode 100644
index 000000000..a1623ec00
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "github.com/Sirupsen/logrus"
+)
+
+var log = logrus.New()
+
+func init() {
+ log.Formatter = new(logrus.JSONFormatter)
+ log.Formatter = new(logrus.TextFormatter) // default
+ log.Level = logrus.DebugLevel
+}
+
+func main() {
+ defer func() {
+ err := recover()
+ if err != nil {
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "err": err,
+ "number": 100,
+ }).Fatal("The ice breaks!")
+ }
+ }()
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "number": 8,
+ }).Debug("Started observing beach")
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(logrus.Fields{
+ "temperature": -4,
+ }).Debug("Temperature changes")
+
+ log.WithFields(logrus.Fields{
+ "animal": "orca",
+ "size": 9009,
+ }).Panic("It's over 9000!")
+}
diff --git a/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go b/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
new file mode 100644
index 000000000..3187f6d3e
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ "github.com/Sirupsen/logrus"
+ "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
+)
+
+var log = logrus.New()
+
+func init() {
+ log.Formatter = new(logrus.TextFormatter) // default
+ log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
+}
+
+func main() {
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 100,
+ }).Fatal("The ice breaks!")
+}
diff --git a/vendor/github.com/Sirupsen/logrus/exported.go b/vendor/github.com/Sirupsen/logrus/exported.go
new file mode 100644
index 000000000..9a0120ac1
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/exported.go
@@ -0,0 +1,193 @@
+package logrus
+
+import (
+ "io"
+)
+
+var (
+ // std is the name of the standard logger in stdlib `log`
+ std = New()
+)
+
+func StandardLogger() *Logger {
+ return std
+}
+
+// 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 {
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ 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)
+}
+
+// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
+func WithError(err error) *Entry {
+ return std.WithField(ErrorKey, err)
+}
+
+// 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...)
+}
diff --git a/vendor/github.com/Sirupsen/logrus/formatter.go b/vendor/github.com/Sirupsen/logrus/formatter.go
new file mode 100644
index 000000000..104d689f1
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/formatter.go
@@ -0,0 +1,48 @@
+package logrus
+
+import "time"
+
+const DefaultTimestampFormat = time.RFC3339
+
+// 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"]
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
new file mode 100644
index 000000000..c6d290c77
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
@@ -0,0 +1,98 @@
+package logrus
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+// smallFields is a small size data set for benchmarking
+var smallFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+}
+
+// largeFields is a large size data set for benchmarking
+var largeFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+ "five": "six",
+ "seven": "eight",
+ "nine": "ten",
+ "eleven": "twelve",
+ "thirteen": "fourteen",
+ "fifteen": "sixteen",
+ "seventeen": "eighteen",
+ "nineteen": "twenty",
+ "a": "b",
+ "c": "d",
+ "e": "f",
+ "g": "h",
+ "i": "j",
+ "k": "l",
+ "m": "n",
+ "o": "p",
+ "q": "r",
+ "s": "t",
+ "u": "v",
+ "w": "x",
+ "y": "z",
+ "this": "will",
+ "make": "thirty",
+ "entries": "yeah",
+}
+
+var errorFields = Fields{
+ "foo": fmt.Errorf("bar"),
+ "baz": fmt.Errorf("qux"),
+}
+
+func BenchmarkErrorTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
+}
+
+func BenchmarkSmallTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
+}
+
+func BenchmarkLargeTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
+}
+
+func BenchmarkSmallColoredTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
+}
+
+func BenchmarkLargeColoredTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
+}
+
+func BenchmarkSmallJSONFormatter(b *testing.B) {
+ doBenchmark(b, &JSONFormatter{}, smallFields)
+}
+
+func BenchmarkLargeJSONFormatter(b *testing.B) {
+ doBenchmark(b, &JSONFormatter{}, largeFields)
+}
+
+func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
+ entry := &Entry{
+ Time: time.Time{},
+ Level: InfoLevel,
+ Message: "message",
+ Data: fields,
+ }
+ var d []byte
+ var err error
+ for i := 0; i < b.N; i++ {
+ d, err = formatter.Format(entry)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(len(d)))
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go b/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
new file mode 100644
index 000000000..8ea93ddf2
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
@@ -0,0 +1,56 @@
+package logstash
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/Sirupsen/logrus"
+)
+
+// Formatter generates json in logstash format.
+// Logstash site: http://logstash.net/
+type LogstashFormatter struct {
+ Type string // if not empty use for logstash type field.
+
+ // TimestampFormat sets the format used for timestamps.
+ TimestampFormat string
+}
+
+func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ entry.Data["@version"] = 1
+
+ if f.TimestampFormat == "" {
+ f.TimestampFormat = logrus.DefaultTimestampFormat
+ }
+
+ entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat)
+
+ // set message field
+ v, ok := entry.Data["message"]
+ if ok {
+ entry.Data["fields.message"] = v
+ }
+ entry.Data["message"] = entry.Message
+
+ // set level field
+ v, ok = entry.Data["level"]
+ if ok {
+ entry.Data["fields.level"] = v
+ }
+ entry.Data["level"] = entry.Level.String()
+
+ // set type field
+ if f.Type != "" {
+ v, ok = entry.Data["type"]
+ if ok {
+ entry.Data["fields.type"] = v
+ }
+ entry.Data["type"] = f.Type
+ }
+
+ 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
+}
diff --git a/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go b/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
new file mode 100644
index 000000000..d8814a0ea
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
@@ -0,0 +1,52 @@
+package logstash
+
+import (
+ "bytes"
+ "encoding/json"
+ "github.com/Sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestLogstashFormatter(t *testing.T) {
+ assert := assert.New(t)
+
+ lf := LogstashFormatter{Type: "abc"}
+
+ fields := logrus.Fields{
+ "message": "def",
+ "level": "ijk",
+ "type": "lmn",
+ "one": 1,
+ "pi": 3.14,
+ "bool": true,
+ }
+
+ entry := logrus.WithFields(fields)
+ entry.Message = "msg"
+ entry.Level = logrus.InfoLevel
+
+ b, _ := lf.Format(entry)
+
+ var data map[string]interface{}
+ dec := json.NewDecoder(bytes.NewReader(b))
+ dec.UseNumber()
+ dec.Decode(&data)
+
+ // base fields
+ assert.Equal(json.Number("1"), data["@version"])
+ assert.NotEmpty(data["@timestamp"])
+ assert.Equal("abc", data["type"])
+ assert.Equal("msg", data["message"])
+ assert.Equal("info", data["level"])
+
+ // substituted fields
+ assert.Equal("def", data["fields.message"])
+ assert.Equal("ijk", data["fields.level"])
+ assert.Equal("lmn", data["fields.type"])
+
+ // formats
+ assert.Equal(json.Number("1"), data["one"])
+ assert.Equal(json.Number("3.14"), data["pi"])
+ assert.Equal(true, data["bool"])
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hook_test.go b/vendor/github.com/Sirupsen/logrus/hook_test.go
new file mode 100644
index 000000000..13f34cb6f
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hook_test.go
@@ -0,0 +1,122 @@
+package logrus
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type TestHook struct {
+ Fired bool
+}
+
+func (hook *TestHook) Fire(entry *Entry) error {
+ hook.Fired = true
+ return nil
+}
+
+func (hook *TestHook) Levels() []Level {
+ return []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ PanicLevel,
+ }
+}
+
+func TestHookFires(t *testing.T) {
+ hook := new(TestHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ assert.Equal(t, hook.Fired, false)
+
+ log.Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, true)
+ })
+}
+
+type ModifyHook struct {
+}
+
+func (hook *ModifyHook) Fire(entry *Entry) error {
+ entry.Data["wow"] = "whale"
+ return nil
+}
+
+func (hook *ModifyHook) Levels() []Level {
+ return []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ PanicLevel,
+ }
+}
+
+func TestHookCanModifyEntry(t *testing.T) {
+ hook := new(ModifyHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.WithField("wow", "elephant").Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["wow"], "whale")
+ })
+}
+
+func TestCanFireMultipleHooks(t *testing.T) {
+ hook1 := new(ModifyHook)
+ hook2 := new(TestHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook1)
+ log.Hooks.Add(hook2)
+
+ log.WithField("wow", "elephant").Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["wow"], "whale")
+ assert.Equal(t, hook2.Fired, true)
+ })
+}
+
+type ErrorHook struct {
+ Fired bool
+}
+
+func (hook *ErrorHook) Fire(entry *Entry) error {
+ hook.Fired = true
+ return nil
+}
+
+func (hook *ErrorHook) Levels() []Level {
+ return []Level{
+ ErrorLevel,
+ }
+}
+
+func TestErrorHookShouldntFireOnInfo(t *testing.T) {
+ hook := new(ErrorHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, false)
+ })
+}
+
+func TestErrorHookShouldFireOnError(t *testing.T) {
+ hook := new(ErrorHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.Error("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, true)
+ })
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks.go b/vendor/github.com/Sirupsen/logrus/hooks.go
new file mode 100644
index 000000000..3f151cdc3
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks.go
@@ -0,0 +1,34 @@
+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
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md b/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
new file mode 100644
index 000000000..066704b37
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
@@ -0,0 +1,39 @@
+# Syslog Hooks for Logrus
+
+## Usage
+
+```go
+import (
+ "log/syslog"
+ "github.com/Sirupsen/logrus"
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
+
+If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
+
+```go
+import (
+ "log/syslog"
+ "github.com/Sirupsen/logrus"
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
\ No newline at end of file
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
new file mode 100644
index 000000000..c59f331d1
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
@@ -0,0 +1,61 @@
+// +build !windows,!nacl,!plan9
+
+package logrus_syslog
+
+import (
+ "fmt"
+ "github.com/Sirupsen/logrus"
+ "log/syslog"
+ "os"
+)
+
+// SyslogHook to send logs via syslog.
+type SyslogHook struct {
+ Writer *syslog.Writer
+ SyslogNetwork string
+ SyslogRaddr string
+}
+
+// Creates a hook to be added to an instance of logger. This is called with
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
+// `if err == nil { log.Hooks.Add(hook) }`
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
+ w, err := syslog.Dial(network, raddr, priority, tag)
+ return &SyslogHook{w, network, raddr}, err
+}
+
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
+ line, err := entry.String()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
+ return err
+ }
+
+ switch entry.Level {
+ case logrus.PanicLevel:
+ return hook.Writer.Crit(line)
+ case logrus.FatalLevel:
+ return hook.Writer.Crit(line)
+ case logrus.ErrorLevel:
+ return hook.Writer.Err(line)
+ case logrus.WarnLevel:
+ return hook.Writer.Warning(line)
+ case logrus.InfoLevel:
+ return hook.Writer.Info(line)
+ case logrus.DebugLevel:
+ return hook.Writer.Debug(line)
+ default:
+ return nil
+ }
+}
+
+func (hook *SyslogHook) Levels() []logrus.Level {
+ return []logrus.Level{
+ logrus.PanicLevel,
+ logrus.FatalLevel,
+ logrus.ErrorLevel,
+ logrus.WarnLevel,
+ logrus.InfoLevel,
+ logrus.DebugLevel,
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
new file mode 100644
index 000000000..42762dc10
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
@@ -0,0 +1,26 @@
+package logrus_syslog
+
+import (
+ "github.com/Sirupsen/logrus"
+ "log/syslog"
+ "testing"
+)
+
+func TestLocalhostAddAndPrint(t *testing.T) {
+ log := logrus.New()
+ hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err != nil {
+ t.Errorf("Unable to connect to local syslog.")
+ }
+
+ log.Hooks.Add(hook)
+
+ for _, level := range hook.Levels() {
+ if len(log.Hooks[level]) != 1 {
+ t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
+ }
+ }
+
+ log.Info("Congratulations!")
+}
diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go
new file mode 100644
index 000000000..2ad6dc5cf
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/json_formatter.go
@@ -0,0 +1,41 @@
+package logrus
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+type JSONFormatter struct {
+ // TimestampFormat sets the format used for marshaling timestamps.
+ TimestampFormat string
+}
+
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+ data := make(Fields, len(entry.Data)+3)
+ for k, v := range entry.Data {
+ switch v := v.(type) {
+ case error:
+ // Otherwise errors are ignored by `encoding/json`
+ // https://github.com/Sirupsen/logrus/issues/137
+ data[k] = v.Error()
+ default:
+ data[k] = v
+ }
+ }
+ prefixFieldClashes(data)
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = DefaultTimestampFormat
+ }
+
+ data["time"] = entry.Time.Format(timestampFormat)
+ 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
+}
diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter_test.go b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go
new file mode 100644
index 000000000..1d7087325
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go
@@ -0,0 +1,120 @@
+package logrus
+
+import (
+ "encoding/json"
+ "errors"
+
+ "testing"
+)
+
+func TestErrorNotLost(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["error"] != "wild walrus" {
+ t.Fatal("Error field not set")
+ }
+}
+
+func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["omg"] != "wild walrus" {
+ t.Fatal("Error field not set")
+ }
+}
+
+func TestFieldClashWithTime(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("time", "right now!"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.time"] != "right now!" {
+ t.Fatal("fields.time not set to original time field")
+ }
+
+ if entry["time"] != "0001-01-01T00:00:00Z" {
+ t.Fatal("time field not set to current time, was: ", entry["time"])
+ }
+}
+
+func TestFieldClashWithMsg(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("msg", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.msg"] != "something" {
+ t.Fatal("fields.msg not set to original msg field")
+ }
+}
+
+func TestFieldClashWithLevel(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.level"] != "something" {
+ t.Fatal("fields.level not set to original level field")
+ }
+}
+
+func TestJSONEntryEndsWithNewline(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ if b[len(b)-1] != '\n' {
+ t.Fatal("Expected JSON log entry to end with a newline")
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/logger.go b/vendor/github.com/Sirupsen/logrus/logger.go
new file mode 100644
index 000000000..2fdb23176
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/logger.go
@@ -0,0 +1,212 @@
+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.Stderr`. 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.Stderr,
+ 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.
+// If 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)
+}
+
+// Add an error as single field to the log entry. All it does is call
+// `WithError` for the given `error`.
+func (logger *Logger) WithError(err error) *Entry {
+ return NewEntry(logger).WithError(err)
+}
+
+func (logger *Logger) Debugf(format string, args ...interface{}) {
+ if logger.Level >= DebugLevel {
+ NewEntry(logger).Debugf(format, args...)
+ }
+}
+
+func (logger *Logger) Infof(format string, args ...interface{}) {
+ if logger.Level >= InfoLevel {
+ 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{}) {
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnf(format, args...)
+ }
+}
+
+func (logger *Logger) Warningf(format string, args ...interface{}) {
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnf(format, args...)
+ }
+}
+
+func (logger *Logger) Errorf(format string, args ...interface{}) {
+ if logger.Level >= ErrorLevel {
+ NewEntry(logger).Errorf(format, args...)
+ }
+}
+
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
+ if logger.Level >= FatalLevel {
+ NewEntry(logger).Fatalf(format, args...)
+ }
+ os.Exit(1)
+}
+
+func (logger *Logger) Panicf(format string, args ...interface{}) {
+ if logger.Level >= PanicLevel {
+ NewEntry(logger).Panicf(format, args...)
+ }
+}
+
+func (logger *Logger) Debug(args ...interface{}) {
+ if logger.Level >= DebugLevel {
+ NewEntry(logger).Debug(args...)
+ }
+}
+
+func (logger *Logger) Info(args ...interface{}) {
+ if logger.Level >= InfoLevel {
+ NewEntry(logger).Info(args...)
+ }
+}
+
+func (logger *Logger) Print(args ...interface{}) {
+ NewEntry(logger).Info(args...)
+}
+
+func (logger *Logger) Warn(args ...interface{}) {
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warn(args...)
+ }
+}
+
+func (logger *Logger) Warning(args ...interface{}) {
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warn(args...)
+ }
+}
+
+func (logger *Logger) Error(args ...interface{}) {
+ if logger.Level >= ErrorLevel {
+ NewEntry(logger).Error(args...)
+ }
+}
+
+func (logger *Logger) Fatal(args ...interface{}) {
+ if logger.Level >= FatalLevel {
+ NewEntry(logger).Fatal(args...)
+ }
+ os.Exit(1)
+}
+
+func (logger *Logger) Panic(args ...interface{}) {
+ if logger.Level >= PanicLevel {
+ NewEntry(logger).Panic(args...)
+ }
+}
+
+func (logger *Logger) Debugln(args ...interface{}) {
+ if logger.Level >= DebugLevel {
+ NewEntry(logger).Debugln(args...)
+ }
+}
+
+func (logger *Logger) Infoln(args ...interface{}) {
+ if logger.Level >= InfoLevel {
+ NewEntry(logger).Infoln(args...)
+ }
+}
+
+func (logger *Logger) Println(args ...interface{}) {
+ NewEntry(logger).Println(args...)
+}
+
+func (logger *Logger) Warnln(args ...interface{}) {
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnln(args...)
+ }
+}
+
+func (logger *Logger) Warningln(args ...interface{}) {
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnln(args...)
+ }
+}
+
+func (logger *Logger) Errorln(args ...interface{}) {
+ if logger.Level >= ErrorLevel {
+ NewEntry(logger).Errorln(args...)
+ }
+}
+
+func (logger *Logger) Fatalln(args ...interface{}) {
+ if logger.Level >= FatalLevel {
+ NewEntry(logger).Fatalln(args...)
+ }
+ os.Exit(1)
+}
+
+func (logger *Logger) Panicln(args ...interface{}) {
+ if logger.Level >= PanicLevel {
+ NewEntry(logger).Panicln(args...)
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/logrus.go b/vendor/github.com/Sirupsen/logrus/logrus.go
new file mode 100644
index 000000000..0c09fbc26
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/logrus.go
@@ -0,0 +1,98 @@
+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 = &Entry{}
+ _ StdLogger = &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{})
+}
diff --git a/vendor/github.com/Sirupsen/logrus/logrus_test.go b/vendor/github.com/Sirupsen/logrus/logrus_test.go
new file mode 100644
index 000000000..efaacea23
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/logrus_test.go
@@ -0,0 +1,301 @@
+package logrus
+
+import (
+ "bytes"
+ "encoding/json"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ log(logger)
+
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ assertions(fields)
+}
+
+func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
+ var buffer bytes.Buffer
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = &TextFormatter{
+ DisableColors: true,
+ }
+
+ log(logger)
+
+ fields := make(map[string]string)
+ for _, kv := range strings.Split(buffer.String(), " ") {
+ if !strings.Contains(kv, "=") {
+ continue
+ }
+ kvArr := strings.Split(kv, "=")
+ key := strings.TrimSpace(kvArr[0])
+ val := kvArr[1]
+ if kvArr[1][0] == '"' {
+ var err error
+ val, err = strconv.Unquote(val)
+ assert.NoError(t, err)
+ }
+ fields[key] = val
+ }
+ assertions(fields)
+}
+
+func TestPrint(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "info")
+ })
+}
+
+func TestInfo(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "info")
+ })
+}
+
+func TestWarn(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Warn("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "warning")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln("test", "test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test test")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln("test", 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test 10")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln(10, 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "10 10")
+ })
+}
+
+func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln(10, 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "10 10")
+ })
+}
+
+func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test", 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test10")
+ })
+}
+
+func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test", "test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "testtest")
+ })
+}
+
+func TestWithFieldsShouldAllowAssignments(t *testing.T) {
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ localLog := logger.WithFields(Fields{
+ "key1": "value1",
+ })
+
+ localLog.WithField("key2", "value2").Info("test")
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ assert.Equal(t, "value2", fields["key2"])
+ assert.Equal(t, "value1", fields["key1"])
+
+ buffer = bytes.Buffer{}
+ fields = Fields{}
+ localLog.Info("test")
+ err = json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ _, ok := fields["key2"]
+ assert.Equal(t, false, ok)
+ assert.Equal(t, "value1", fields["key1"])
+}
+
+func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("msg", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ })
+}
+
+func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("msg", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["fields.msg"], "hello")
+ })
+}
+
+func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("time", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["fields.time"], "hello")
+ })
+}
+
+func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("level", 1).Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["level"], "info")
+ assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
+ })
+}
+
+func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
+ LogAndAssertText(t, func(log *Logger) {
+ ll := log.WithField("herp", "derp")
+ ll.Info("hello")
+ ll.Info("bye")
+ }, func(fields map[string]string) {
+ for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
+ if _, ok := fields[fieldName]; ok {
+ t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
+ }
+ }
+ })
+}
+
+func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
+
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ llog := logger.WithField("context", "eating raw fish")
+
+ llog.Info("looks delicious")
+
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.NoError(t, err, "should have decoded first message")
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
+ assert.Equal(t, fields["msg"], "looks delicious")
+ assert.Equal(t, fields["context"], "eating raw fish")
+
+ buffer.Reset()
+
+ llog.Warn("omg it is!")
+
+ err = json.Unmarshal(buffer.Bytes(), &fields)
+ assert.NoError(t, err, "should have decoded second message")
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
+ assert.Equal(t, fields["msg"], "omg it is!")
+ assert.Equal(t, fields["context"], "eating raw fish")
+ assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
+
+}
+
+func TestConvertLevelToString(t *testing.T) {
+ assert.Equal(t, "debug", DebugLevel.String())
+ assert.Equal(t, "info", InfoLevel.String())
+ assert.Equal(t, "warning", WarnLevel.String())
+ assert.Equal(t, "error", ErrorLevel.String())
+ assert.Equal(t, "fatal", FatalLevel.String())
+ assert.Equal(t, "panic", PanicLevel.String())
+}
+
+func TestParseLevel(t *testing.T) {
+ l, err := ParseLevel("panic")
+ assert.Nil(t, err)
+ assert.Equal(t, PanicLevel, l)
+
+ l, err = ParseLevel("fatal")
+ assert.Nil(t, err)
+ assert.Equal(t, FatalLevel, l)
+
+ l, err = ParseLevel("error")
+ assert.Nil(t, err)
+ assert.Equal(t, ErrorLevel, l)
+
+ l, err = ParseLevel("warn")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("warning")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("info")
+ assert.Nil(t, err)
+ assert.Equal(t, InfoLevel, l)
+
+ l, err = ParseLevel("debug")
+ assert.Nil(t, err)
+ assert.Equal(t, DebugLevel, l)
+
+ l, err = ParseLevel("invalid")
+ assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
+}
+
+func TestGetSetLevelRace(t *testing.T) {
+ wg := sync.WaitGroup{}
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+ if i%2 == 0 {
+ SetLevel(InfoLevel)
+ } else {
+ GetLevel()
+ }
+ }(i)
+
+ }
+ wg.Wait()
+}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_bsd.go b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
new file mode 100644
index 000000000..71f8d67a5
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
@@ -0,0 +1,9 @@
+// +build darwin freebsd openbsd netbsd dragonfly
+
+package logrus
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TIOCGETA
+
+type Termios syscall.Termios
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_linux.go b/vendor/github.com/Sirupsen/logrus/terminal_linux.go
new file mode 100644
index 000000000..a2c0b40db
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/terminal_linux.go
@@ -0,0 +1,12 @@
+// Based on ssh/terminal:
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logrus
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TCGETS
+
+type Termios syscall.Termios
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
new file mode 100644
index 000000000..4bb537602
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
@@ -0,0 +1,21 @@
+// 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 darwin freebsd openbsd netbsd dragonfly
+
+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
+}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
new file mode 100644
index 000000000..3e70bf7bf
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
@@ -0,0 +1,15 @@
+// +build solaris
+
+package logrus
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal() bool {
+ _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
+ return err == nil
+}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/vendor/github.com/Sirupsen/logrus/terminal_windows.go
new file mode 100644
index 000000000..2e09f6f7e
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/terminal_windows.go
@@ -0,0 +1,27 @@
+// 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
+}
diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter.go b/vendor/github.com/Sirupsen/logrus/text_formatter.go
new file mode 100644
index 000000000..06ef20233
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/text_formatter.go
@@ -0,0 +1,161 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "runtime"
+ "sort"
+ "strings"
+ "time"
+)
+
+const (
+ nocolor = 0
+ red = 31
+ green = 32
+ yellow = 33
+ blue = 34
+ gray = 37
+)
+
+var (
+ baseTimestamp time.Time
+ isTerminal bool
+)
+
+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
+
+ // Force disabling colors.
+ DisableColors bool
+
+ // Disable timestamp logging. useful when output is redirected to logging
+ // system that already adds timestamps.
+ DisableTimestamp bool
+
+ // Enable logging the full timestamp when a TTY is attached instead of just
+ // the time passed since beginning of execution.
+ FullTimestamp bool
+
+ // TimestampFormat to use for display when a full timestamp is printed
+ TimestampFormat string
+
+ // The fields are sorted by default for a consistent output. For applications
+ // that log extremely frequently and don't use the JSON formatter this may not
+ // be desired.
+ DisableSorting bool
+}
+
+func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
+ var keys []string = make([]string, 0, len(entry.Data))
+ for k := range entry.Data {
+ keys = append(keys, k)
+ }
+
+ if !f.DisableSorting {
+ sort.Strings(keys)
+ }
+
+ b := &bytes.Buffer{}
+
+ prefixFieldClashes(entry.Data)
+
+ isColorTerminal := isTerminal && (runtime.GOOS != "windows")
+ isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = DefaultTimestampFormat
+ }
+ if isColored {
+ f.printColored(b, entry, keys, timestampFormat)
+ } else {
+ if !f.DisableTimestamp {
+ f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
+ }
+ f.appendKeyValue(b, "level", entry.Level.String())
+ if entry.Message != "" {
+ 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 (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
+ var levelColor int
+ switch entry.Level {
+ case DebugLevel:
+ levelColor = gray
+ case WarnLevel:
+ levelColor = yellow
+ case ErrorLevel, FatalLevel, PanicLevel:
+ levelColor = red
+ default:
+ levelColor = blue
+ }
+
+ levelText := strings.ToUpper(entry.Level.String())[0:4]
+
+ if !f.FullTimestamp {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
+ } else {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), 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 string, value interface{}) {
+
+ b.WriteString(key)
+ b.WriteByte('=')
+
+ switch value := value.(type) {
+ case string:
+ if needsQuoting(value) {
+ b.WriteString(value)
+ } else {
+ fmt.Fprintf(b, "%q", value)
+ }
+ case error:
+ errmsg := value.Error()
+ if needsQuoting(errmsg) {
+ b.WriteString(errmsg)
+ } else {
+ fmt.Fprintf(b, "%q", value)
+ }
+ default:
+ fmt.Fprint(b, value)
+ }
+
+ b.WriteByte(' ')
+}
diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter_test.go b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go
new file mode 100644
index 000000000..e25a44f67
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go
@@ -0,0 +1,61 @@
+package logrus
+
+import (
+ "bytes"
+ "errors"
+ "testing"
+ "time"
+)
+
+func TestQuoting(t *testing.T) {
+ tf := &TextFormatter{DisableColors: true}
+
+ checkQuoting := func(q bool, value interface{}) {
+ b, _ := tf.Format(WithField("test", value))
+ idx := bytes.Index(b, ([]byte)("test="))
+ cont := bytes.Contains(b[idx+5:], []byte{'"'})
+ if cont != q {
+ if q {
+ t.Errorf("quoting expected for: %#v", value)
+ } else {
+ t.Errorf("quoting not expected for: %#v", value)
+ }
+ }
+ }
+
+ checkQuoting(false, "abcd")
+ checkQuoting(false, "v1.0")
+ checkQuoting(false, "1234567890")
+ checkQuoting(true, "/foobar")
+ checkQuoting(true, "x y")
+ checkQuoting(true, "x,y")
+ checkQuoting(false, errors.New("invalid"))
+ checkQuoting(true, errors.New("invalid argument"))
+}
+
+func TestTimestampFormat(t *testing.T) {
+ checkTimeStr := func(format string) {
+ customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
+ customStr, _ := customFormatter.Format(WithField("test", "test"))
+ timeStart := bytes.Index(customStr, ([]byte)("time="))
+ timeEnd := bytes.Index(customStr, ([]byte)("level="))
+ timeStr := customStr[timeStart+5 : timeEnd-1]
+ if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' {
+ timeStr = timeStr[1 : len(timeStr)-1]
+ }
+ if format == "" {
+ format = time.RFC3339
+ }
+ _, e := time.Parse(format, (string)(timeStr))
+ if e != nil {
+ t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
+ }
+ }
+
+ checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
+ checkTimeStr("Mon Jan _2 15:04:05 2006")
+ checkTimeStr("")
+}
+
+// TODO add tests for sorting etc., this requires a parser for the text
+// formatter output.
diff --git a/vendor/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go
new file mode 100644
index 000000000..1e30b1c75
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/writer.go
@@ -0,0 +1,31 @@
+package logrus
+
+import (
+ "bufio"
+ "io"
+ "runtime"
+)
+
+func (logger *Logger) Writer() *io.PipeWriter {
+ reader, writer := io.Pipe()
+
+ go logger.writerScanner(reader)
+ runtime.SetFinalizer(writer, writerFinalizer)
+
+ return writer
+}
+
+func (logger *Logger) writerScanner(reader *io.PipeReader) {
+ scanner := bufio.NewScanner(reader)
+ for scanner.Scan() {
+ logger.Print(scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ logger.Errorf("Error while reading from Writer: %s", err)
+ }
+ reader.Close()
+}
+
+func writerFinalizer(writer *io.PipeWriter) {
+ writer.Close()
+}
diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir.go b/vendor/github.com/docker/docker/pkg/homedir/homedir.go
new file mode 100644
index 000000000..8154e83f0
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/homedir/homedir.go
@@ -0,0 +1,39 @@
+package homedir
+
+import (
+ "os"
+ "runtime"
+
+ "github.com/opencontainers/runc/libcontainer/user"
+)
+
+// Key returns the env var name for the user's home dir based on
+// the platform being run on
+func Key() string {
+ if runtime.GOOS == "windows" {
+ return "USERPROFILE"
+ }
+ return "HOME"
+}
+
+// Get returns the home directory of the current user with the help of
+// environment variables depending on the target operating system.
+// Returned path should be used with "path/filepath" to form new paths.
+func Get() string {
+ home := os.Getenv(Key())
+ if home == "" && runtime.GOOS != "windows" {
+ if u, err := user.CurrentUser(); err == nil {
+ return u.Home
+ }
+ }
+ return home
+}
+
+// GetShortcutString returns the string that is shortcut to user's home directory
+// in the native shell of the platform running on.
+func GetShortcutString() string {
+ if runtime.GOOS == "windows" {
+ return "%USERPROFILE%" // be careful while using in format functions
+ }
+ return "~"
+}
diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_test.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_test.go
new file mode 100644
index 000000000..7a95cb2bd
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/homedir/homedir_test.go
@@ -0,0 +1,24 @@
+package homedir
+
+import (
+ "path/filepath"
+ "testing"
+)
+
+func TestGet(t *testing.T) {
+ home := Get()
+ if home == "" {
+ t.Fatal("returned home directory is empty")
+ }
+
+ if !filepath.IsAbs(home) {
+ t.Fatalf("returned path is not absolute: %s", home)
+ }
+}
+
+func TestGetShortcutString(t *testing.T) {
+ shortcut := GetShortcutString()
+ if shortcut == "" {
+ t.Fatal("returned shortcut string is empty")
+ }
+}
diff --git a/vendor/github.com/docker/docker/pkg/mflag/LICENSE b/vendor/github.com/docker/docker/pkg/mflag/LICENSE
new file mode 100644
index 000000000..ac74d8f04
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/mflag/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2014-2015 The Docker & Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/docker/docker/pkg/mflag/README.md b/vendor/github.com/docker/docker/pkg/mflag/README.md
new file mode 100644
index 000000000..da00efa33
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/mflag/README.md
@@ -0,0 +1,40 @@
+Package mflag (aka multiple-flag) implements command-line flag parsing.
+It's an **hacky** fork of the [official golang package](http://golang.org/pkg/flag/)
+
+It adds:
+
+* both short and long flag version
+`./example -s red` `./example --string blue`
+
+* multiple names for the same option
+```
+$>./example -h
+Usage of example:
+ -s, --string="": a simple string
+```
+
+___
+It is very flexible on purpose, so you can do things like:
+```
+$>./example -h
+Usage of example:
+ -s, -string, --string="": a simple string
+```
+
+Or:
+```
+$>./example -h
+Usage of example:
+ -oldflag, --newflag="": a simple string
+```
+
+You can also hide some flags from the usage, so if we want only `--newflag`:
+```
+$>./example -h
+Usage of example:
+ --newflag="": a simple string
+$>./example -oldflag str
+str
+```
+
+See [example.go](example/example.go) for more details.
diff --git a/vendor/github.com/docker/docker/pkg/mflag/example/example.go b/vendor/github.com/docker/docker/pkg/mflag/example/example.go
new file mode 100644
index 000000000..2e766dd1e
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/mflag/example/example.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "fmt"
+
+ flag "github.com/docker/docker/pkg/mflag"
+)
+
+var (
+ i int
+ str string
+ b, b2, h bool
+)
+
+func init() {
+ flag.Bool([]string{"#hp", "#-halp"}, false, "display the halp")
+ flag.BoolVar(&b, []string{"b", "#bal", "#bol", "-bal"}, false, "a simple bool")
+ flag.BoolVar(&b, []string{"g", "#gil"}, false, "a simple bool")
+ flag.BoolVar(&b2, []string{"#-bool"}, false, "a simple bool")
+ flag.IntVar(&i, []string{"-integer", "-number"}, -1, "a simple integer")
+ flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage
+ flag.BoolVar(&h, []string{"h", "#help", "-help"}, false, "display the help")
+ flag.StringVar(&str, []string{"mode"}, "mode1", "set the mode\nmode1: use the mode1\nmode2: use the mode2\nmode3: use the mode3")
+ flag.Parse()
+}
+func main() {
+ if h {
+ flag.PrintDefaults()
+ } else {
+ fmt.Printf("s/#hidden/-string: %s\n", str)
+ fmt.Printf("b: %t\n", b)
+ fmt.Printf("-bool: %t\n", b2)
+ fmt.Printf("s/#hidden/-string(via lookup): %s\n", flag.Lookup("s").Value.String())
+ fmt.Printf("ARGS: %v\n", flag.Args())
+ }
+}
diff --git a/vendor/github.com/docker/docker/pkg/mflag/flag.go b/vendor/github.com/docker/docker/pkg/mflag/flag.go
new file mode 100644
index 000000000..d430f13a2
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/mflag/flag.go
@@ -0,0 +1,1264 @@
+// Copyright 2014-2015 The Docker & 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 mflag implements command-line flag parsing.
+//
+// Usage:
+//
+// Define flags using flag.String(), Bool(), Int(), etc.
+//
+// This declares an integer flag, -f or --flagname, stored in the pointer ip, with type *int.
+// import "flag /github.com/docker/docker/pkg/mflag"
+// var ip = flag.Int([]string{"f", "-flagname"}, 1234, "help message for flagname")
+// If you like, you can bind the flag to a variable using the Var() functions.
+// var flagvar int
+// func init() {
+// // -flaghidden will work, but will be hidden from the usage
+// flag.IntVar(&flagvar, []string{"f", "#flaghidden", "-flagname"}, 1234, "help message for flagname")
+// }
+// Or you can create custom flags that satisfy the Value interface (with
+// pointer receivers) and couple them to flag parsing by
+// flag.Var(&flagVal, []string{"name"}, "help message for flagname")
+// For such flags, the default value is just the initial value of the variable.
+//
+// You can also add "deprecated" flags, they are still usable, but are not shown
+// in the usage and will display a warning when you try to use them. `#` before
+// an option means this option is deprecated, if there is an following option
+// without `#` ahead, then that's the replacement, if not, it will just be removed:
+// var ip = flag.Int([]string{"#f", "#flagname", "-flagname"}, 1234, "help message for flagname")
+// this will display: `Warning: '-f' is deprecated, it will be replaced by '--flagname' soon. See usage.` or
+// this will display: `Warning: '-flagname' is deprecated, it will be replaced by '--flagname' soon. See usage.`
+// var ip = flag.Int([]string{"f", "#flagname"}, 1234, "help message for flagname")
+// will display: `Warning: '-flagname' is deprecated, it will be removed soon. See usage.`
+// so you can only use `-f`.
+//
+// You can also group one letter flags, bif you declare
+// var v = flag.Bool([]string{"v", "-verbose"}, false, "help message for verbose")
+// var s = flag.Bool([]string{"s", "-slow"}, false, "help message for slow")
+// you will be able to use the -vs or -sv
+//
+// After all flags are defined, call
+// flag.Parse()
+// to parse the command line into the defined flags.
+//
+// Flags may then be used directly. If you're using the flags themselves,
+// they are all pointers; if you bind to variables, they're values.
+// fmt.Println("ip has value ", *ip)
+// fmt.Println("flagvar has value ", flagvar)
+//
+// After parsing, the arguments after the flag are available as the
+// slice flag.Args() or individually as flag.Arg(i).
+// The arguments are indexed from 0 through flag.NArg()-1.
+//
+// Command line flag syntax:
+// -flag
+// -flag=x
+// -flag="x"
+// -flag='x'
+// -flag x // non-boolean flags only
+// One or two minus signs may be used; they are equivalent.
+// The last form is not permitted for boolean flags because the
+// meaning of the command
+// cmd -x *
+// will change if there is a file called 0, false, etc. You must
+// use the -flag=false form to turn off a boolean flag.
+//
+// Flag parsing stops just before the first non-flag argument
+// ("-" is a non-flag argument) or after the terminator "--".
+//
+// Integer flags accept 1234, 0664, 0x1234 and may be negative.
+// Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
+// Duration flags accept any input valid for time.ParseDuration.
+//
+// The default set of command-line flags is controlled by
+// top-level functions. The FlagSet type allows one to define
+// independent sets of flags, such as to implement subcommands
+// in a command-line interface. The methods of FlagSet are
+// analogous to the top-level functions for the command-line
+// flag set.
+
+package mflag
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "text/tabwriter"
+ "time"
+
+ "github.com/docker/docker/pkg/homedir"
+)
+
+// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
+var ErrHelp = errors.New("flag: help requested")
+
+// ErrRetry is the error returned if you need to try letter by letter
+var ErrRetry = errors.New("flag: retry")
+
+// -- bool Value
+type boolValue bool
+
+func newBoolValue(val bool, p *bool) *boolValue {
+ *p = val
+ return (*boolValue)(p)
+}
+
+func (b *boolValue) Set(s string) error {
+ v, err := strconv.ParseBool(s)
+ *b = boolValue(v)
+ return err
+}
+
+func (b *boolValue) Get() interface{} { return bool(*b) }
+
+func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
+
+func (b *boolValue) IsBoolFlag() bool { return true }
+
+// optional interface to indicate boolean flags that can be
+// supplied without "=value" text
+type boolFlag interface {
+ Value
+ IsBoolFlag() bool
+}
+
+// -- int Value
+type intValue int
+
+func newIntValue(val int, p *int) *intValue {
+ *p = val
+ return (*intValue)(p)
+}
+
+func (i *intValue) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
+ *i = intValue(v)
+ return err
+}
+
+func (i *intValue) Get() interface{} { return int(*i) }
+
+func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
+
+// -- int64 Value
+type int64Value int64
+
+func newInt64Value(val int64, p *int64) *int64Value {
+ *p = val
+ return (*int64Value)(p)
+}
+
+func (i *int64Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
+ *i = int64Value(v)
+ return err
+}
+
+func (i *int64Value) Get() interface{} { return int64(*i) }
+
+func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
+
+// -- uint Value
+type uintValue uint
+
+func newUintValue(val uint, p *uint) *uintValue {
+ *p = val
+ return (*uintValue)(p)
+}
+
+func (i *uintValue) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
+ *i = uintValue(v)
+ return err
+}
+
+func (i *uintValue) Get() interface{} { return uint(*i) }
+
+func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
+
+// -- uint64 Value
+type uint64Value uint64
+
+func newUint64Value(val uint64, p *uint64) *uint64Value {
+ *p = val
+ return (*uint64Value)(p)
+}
+
+func (i *uint64Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
+ *i = uint64Value(v)
+ return err
+}
+
+func (i *uint64Value) Get() interface{} { return uint64(*i) }
+
+func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
+
+// -- uint16 Value
+type uint16Value uint16
+
+func newUint16Value(val uint16, p *uint16) *uint16Value {
+ *p = val
+ return (*uint16Value)(p)
+}
+
+func (i *uint16Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 16)
+ *i = uint16Value(v)
+ return err
+}
+
+func (i *uint16Value) Get() interface{} { return uint16(*i) }
+
+func (i *uint16Value) String() string { return fmt.Sprintf("%v", *i) }
+
+// -- string Value
+type stringValue string
+
+func newStringValue(val string, p *string) *stringValue {
+ *p = val
+ return (*stringValue)(p)
+}
+
+func (s *stringValue) Set(val string) error {
+ *s = stringValue(val)
+ return nil
+}
+
+func (s *stringValue) Get() interface{} { return string(*s) }
+
+func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
+
+// -- float64 Value
+type float64Value float64
+
+func newFloat64Value(val float64, p *float64) *float64Value {
+ *p = val
+ return (*float64Value)(p)
+}
+
+func (f *float64Value) Set(s string) error {
+ v, err := strconv.ParseFloat(s, 64)
+ *f = float64Value(v)
+ return err
+}
+
+func (f *float64Value) Get() interface{} { return float64(*f) }
+
+func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// -- time.Duration Value
+type durationValue time.Duration
+
+func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
+ *p = val
+ return (*durationValue)(p)
+}
+
+func (d *durationValue) Set(s string) error {
+ v, err := time.ParseDuration(s)
+ *d = durationValue(v)
+ return err
+}
+
+func (d *durationValue) Get() interface{} { return time.Duration(*d) }
+
+func (d *durationValue) String() string { return (*time.Duration)(d).String() }
+
+// Value is the interface to the dynamic value stored in a flag.
+// (The default value is represented as a string.)
+//
+// If a Value has an IsBoolFlag() bool method returning true,
+// the command-line parser makes -name equivalent to -name=true
+// rather than using the next command-line argument.
+type Value interface {
+ String() string
+ Set(string) error
+}
+
+// Getter is an interface that allows the contents of a Value to be retrieved.
+// It wraps the Value interface, rather than being part of it, because it
+// appeared after Go 1 and its compatibility rules. All Value types provided
+// by this package satisfy the Getter interface.
+type Getter interface {
+ Value
+ Get() interface{}
+}
+
+// ErrorHandling defines how to handle flag parsing errors.
+type ErrorHandling int
+
+// ErrorHandling strategies available when a flag parsing error occurs
+const (
+ ContinueOnError ErrorHandling = iota
+ ExitOnError
+ PanicOnError
+)
+
+// A FlagSet represents a set of defined flags. The zero value of a FlagSet
+// has no name and has ContinueOnError error handling.
+type FlagSet struct {
+ // Usage is the function called when an error occurs while parsing flags.
+ // The field is a function (not a method) that may be changed to point to
+ // a custom error handler.
+ Usage func()
+ ShortUsage func()
+
+ name string
+ parsed bool
+ actual map[string]*Flag
+ formal map[string]*Flag
+ args []string // arguments after flags
+ errorHandling ErrorHandling
+ output io.Writer // nil means stderr; use Out() accessor
+ nArgRequirements []nArgRequirement
+}
+
+// A Flag represents the state of a flag.
+type Flag struct {
+ Names []string // name as it appears on command line
+ Usage string // help message
+ Value Value // value as set
+ DefValue string // default value (as text); for usage message
+}
+
+type flagSlice []string
+
+func (p flagSlice) Len() int { return len(p) }
+func (p flagSlice) Less(i, j int) bool {
+ pi, pj := strings.TrimPrefix(p[i], "-"), strings.TrimPrefix(p[j], "-")
+ lpi, lpj := strings.ToLower(pi), strings.ToLower(pj)
+ if lpi != lpj {
+ return lpi < lpj
+ }
+ return pi < pj
+}
+func (p flagSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+// sortFlags returns the flags as a slice in lexicographical sorted order.
+func sortFlags(flags map[string]*Flag) []*Flag {
+ var list flagSlice
+
+ // The sorted list is based on the first name, when flag map might use the other names.
+ nameMap := make(map[string]string)
+
+ for n, f := range flags {
+ fName := strings.TrimPrefix(f.Names[0], "#")
+ nameMap[fName] = n
+ if len(f.Names) == 1 {
+ list = append(list, fName)
+ continue
+ }
+
+ found := false
+ for _, name := range list {
+ if name == fName {
+ found = true
+ break
+ }
+ }
+ if !found {
+ list = append(list, fName)
+ }
+ }
+ sort.Sort(list)
+ result := make([]*Flag, len(list))
+ for i, name := range list {
+ result[i] = flags[nameMap[name]]
+ }
+ return result
+}
+
+// Name returns the name of the FlagSet.
+func (fs *FlagSet) Name() string {
+ return fs.name
+}
+
+// Out returns the destination for usage and error messages.
+func (fs *FlagSet) Out() io.Writer {
+ if fs.output == nil {
+ return os.Stderr
+ }
+ return fs.output
+}
+
+// SetOutput sets the destination for usage and error messages.
+// If output is nil, os.Stderr is used.
+func (fs *FlagSet) SetOutput(output io.Writer) {
+ fs.output = output
+}
+
+// VisitAll visits the flags in lexicographical order, calling fn for each.
+// It visits all flags, even those not set.
+func (fs *FlagSet) VisitAll(fn func(*Flag)) {
+ for _, flag := range sortFlags(fs.formal) {
+ fn(flag)
+ }
+}
+
+// VisitAll visits the command-line flags in lexicographical order, calling
+// fn for each. It visits all flags, even those not set.
+func VisitAll(fn func(*Flag)) {
+ CommandLine.VisitAll(fn)
+}
+
+// Visit visits the flags in lexicographical order, calling fn for each.
+// It visits only those flags that have been set.
+func (fs *FlagSet) Visit(fn func(*Flag)) {
+ for _, flag := range sortFlags(fs.actual) {
+ fn(flag)
+ }
+}
+
+// Visit visits the command-line flags in lexicographical order, calling fn
+// for each. It visits only those flags that have been set.
+func Visit(fn func(*Flag)) {
+ CommandLine.Visit(fn)
+}
+
+// Lookup returns the Flag structure of the named flag, returning nil if none exists.
+func (fs *FlagSet) Lookup(name string) *Flag {
+ return fs.formal[name]
+}
+
+// IsSet indicates whether the specified flag is set in the given FlagSet
+func (fs *FlagSet) IsSet(name string) bool {
+ return fs.actual[name] != nil
+}
+
+// Lookup returns the Flag structure of the named command-line flag,
+// returning nil if none exists.
+func Lookup(name string) *Flag {
+ return CommandLine.formal[name]
+}
+
+// IsSet indicates whether the specified flag was specified at all on the cmd line.
+func IsSet(name string) bool {
+ return CommandLine.IsSet(name)
+}
+
+type nArgRequirementType int
+
+// Indicator used to pass to BadArgs function
+const (
+ Exact nArgRequirementType = iota
+ Max
+ Min
+)
+
+type nArgRequirement struct {
+ Type nArgRequirementType
+ N int
+}
+
+// Require adds a requirement about the number of arguments for the FlagSet.
+// The first parameter can be Exact, Max, or Min to respectively specify the exact,
+// the maximum, or the minimal number of arguments required.
+// The actual check is done in FlagSet.CheckArgs().
+func (fs *FlagSet) Require(nArgRequirementType nArgRequirementType, nArg int) {
+ fs.nArgRequirements = append(fs.nArgRequirements, nArgRequirement{nArgRequirementType, nArg})
+}
+
+// CheckArgs uses the requirements set by FlagSet.Require() to validate
+// the number of arguments. If the requirements are not met,
+// an error message string is returned.
+func (fs *FlagSet) CheckArgs() (message string) {
+ for _, req := range fs.nArgRequirements {
+ var arguments string
+ if req.N == 1 {
+ arguments = "1 argument"
+ } else {
+ arguments = fmt.Sprintf("%d arguments", req.N)
+ }
+
+ str := func(kind string) string {
+ return fmt.Sprintf("%q requires %s%s", fs.name, kind, arguments)
+ }
+
+ switch req.Type {
+ case Exact:
+ if fs.NArg() != req.N {
+ return str("")
+ }
+ case Max:
+ if fs.NArg() > req.N {
+ return str("a maximum of ")
+ }
+ case Min:
+ if fs.NArg() < req.N {
+ return str("a minimum of ")
+ }
+ }
+ }
+ return ""
+}
+
+// Set sets the value of the named flag.
+func (fs *FlagSet) Set(name, value string) error {
+ flag, ok := fs.formal[name]
+ if !ok {
+ return fmt.Errorf("no such flag -%v", name)
+ }
+ if err := flag.Value.Set(value); err != nil {
+ return err
+ }
+ if fs.actual == nil {
+ fs.actual = make(map[string]*Flag)
+ }
+ fs.actual[name] = flag
+ return nil
+}
+
+// Set sets the value of the named command-line flag.
+func Set(name, value string) error {
+ return CommandLine.Set(name, value)
+}
+
+// isZeroValue guesses whether the string represents the zero
+// value for a flag. It is not accurate but in practice works OK.
+func isZeroValue(value string) bool {
+ switch value {
+ case "false":
+ return true
+ case "":
+ return true
+ case "0":
+ return true
+ }
+ return false
+}
+
+// PrintDefaults prints, to standard error unless configured
+// otherwise, the default values of all defined flags in the set.
+func (fs *FlagSet) PrintDefaults() {
+ writer := tabwriter.NewWriter(fs.Out(), 20, 1, 3, ' ', 0)
+ home := homedir.Get()
+
+ // Don't substitute when HOME is /
+ if runtime.GOOS != "windows" && home == "/" {
+ home = ""
+ }
+
+ // Add a blank line between cmd description and list of options
+ if fs.FlagCount() > 0 {
+ fmt.Fprintln(writer, "")
+ }
+
+ fs.VisitAll(func(flag *Flag) {
+ names := []string{}
+ for _, name := range flag.Names {
+ if name[0] != '#' {
+ names = append(names, name)
+ }
+ }
+ if len(names) > 0 && len(flag.Usage) > 0 {
+ val := flag.DefValue
+
+ if home != "" && strings.HasPrefix(val, home) {
+ val = homedir.GetShortcutString() + val[len(home):]
+ }
+
+ if isZeroValue(val) {
+ format := " -%s"
+ fmt.Fprintf(writer, format, strings.Join(names, ", -"))
+ } else {
+ format := " -%s=%s"
+ fmt.Fprintf(writer, format, strings.Join(names, ", -"), val)
+ }
+ for i, line := range strings.Split(flag.Usage, "\n") {
+ if i != 0 {
+ line = " " + line
+ }
+ fmt.Fprintln(writer, "\t", line)
+ }
+ }
+ })
+ writer.Flush()
+}
+
+// PrintDefaults prints to standard error the default values of all defined command-line flags.
+func PrintDefaults() {
+ CommandLine.PrintDefaults()
+}
+
+// defaultUsage is the default function to print a usage message.
+func defaultUsage(fs *FlagSet) {
+ if fs.name == "" {
+ fmt.Fprintf(fs.Out(), "Usage:\n")
+ } else {
+ fmt.Fprintf(fs.Out(), "Usage of %s:\n", fs.name)
+ }
+ fs.PrintDefaults()
+}
+
+// NOTE: Usage is not just defaultUsage(CommandLine)
+// because it serves (via godoc flag Usage) as the example
+// for how to write your own usage function.
+
+// Usage prints to standard error a usage message documenting all defined command-line flags.
+// The function is a variable that may be changed to point to a custom function.
+var Usage = func() {
+ fmt.Fprintf(CommandLine.Out(), "Usage of %s:\n", os.Args[0])
+ PrintDefaults()
+}
+
+// ShortUsage prints to standard error a usage message documenting the standard command layout
+// The function is a variable that may be changed to point to a custom function.
+var ShortUsage = func() {
+ fmt.Fprintf(CommandLine.output, "Usage of %s:\n", os.Args[0])
+}
+
+// FlagCount returns the number of flags that have been defined.
+func (fs *FlagSet) FlagCount() int { return len(sortFlags(fs.formal)) }
+
+// FlagCountUndeprecated returns the number of undeprecated flags that have been defined.
+func (fs *FlagSet) FlagCountUndeprecated() int {
+ count := 0
+ for _, flag := range sortFlags(fs.formal) {
+ for _, name := range flag.Names {
+ if name[0] != '#' {
+ count++
+ break
+ }
+ }
+ }
+ return count
+}
+
+// NFlag returns the number of flags that have been set.
+func (fs *FlagSet) NFlag() int { return len(fs.actual) }
+
+// NFlag returns the number of command-line flags that have been set.
+func NFlag() int { return len(CommandLine.actual) }
+
+// Arg returns the i'th argument. Arg(0) is the first remaining argument
+// after flags have been processed.
+func (fs *FlagSet) Arg(i int) string {
+ if i < 0 || i >= len(fs.args) {
+ return ""
+ }
+ return fs.args[i]
+}
+
+// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
+// after flags have been processed.
+func Arg(i int) string {
+ return CommandLine.Arg(i)
+}
+
+// NArg is the number of arguments remaining after flags have been processed.
+func (fs *FlagSet) NArg() int { return len(fs.args) }
+
+// NArg is the number of arguments remaining after flags have been processed.
+func NArg() int { return len(CommandLine.args) }
+
+// Args returns the non-flag arguments.
+func (fs *FlagSet) Args() []string { return fs.args }
+
+// Args returns the non-flag command-line arguments.
+func Args() []string { return CommandLine.args }
+
+// BoolVar defines a bool flag with specified name, default value, and usage string.
+// The argument p points to a bool variable in which to store the value of the flag.
+func (fs *FlagSet) BoolVar(p *bool, names []string, value bool, usage string) {
+ fs.Var(newBoolValue(value, p), names, usage)
+}
+
+// BoolVar defines a bool flag with specified name, default value, and usage string.
+// The argument p points to a bool variable in which to store the value of the flag.
+func BoolVar(p *bool, names []string, value bool, usage string) {
+ CommandLine.Var(newBoolValue(value, p), names, usage)
+}
+
+// Bool defines a bool flag with specified name, default value, and usage string.
+// The return value is the address of a bool variable that stores the value of the flag.
+func (fs *FlagSet) Bool(names []string, value bool, usage string) *bool {
+ p := new(bool)
+ fs.BoolVar(p, names, value, usage)
+ return p
+}
+
+// Bool defines a bool flag with specified name, default value, and usage string.
+// The return value is the address of a bool variable that stores the value of the flag.
+func Bool(names []string, value bool, usage string) *bool {
+ return CommandLine.Bool(names, value, usage)
+}
+
+// IntVar defines an int flag with specified name, default value, and usage string.
+// The argument p points to an int variable in which to store the value of the flag.
+func (fs *FlagSet) IntVar(p *int, names []string, value int, usage string) {
+ fs.Var(newIntValue(value, p), names, usage)
+}
+
+// IntVar defines an int flag with specified name, default value, and usage string.
+// The argument p points to an int variable in which to store the value of the flag.
+func IntVar(p *int, names []string, value int, usage string) {
+ CommandLine.Var(newIntValue(value, p), names, usage)
+}
+
+// Int defines an int flag with specified name, default value, and usage string.
+// The return value is the address of an int variable that stores the value of the flag.
+func (fs *FlagSet) Int(names []string, value int, usage string) *int {
+ p := new(int)
+ fs.IntVar(p, names, value, usage)
+ return p
+}
+
+// Int defines an int flag with specified name, default value, and usage string.
+// The return value is the address of an int variable that stores the value of the flag.
+func Int(names []string, value int, usage string) *int {
+ return CommandLine.Int(names, value, usage)
+}
+
+// Int64Var defines an int64 flag with specified name, default value, and usage string.
+// The argument p points to an int64 variable in which to store the value of the flag.
+func (fs *FlagSet) Int64Var(p *int64, names []string, value int64, usage string) {
+ fs.Var(newInt64Value(value, p), names, usage)
+}
+
+// Int64Var defines an int64 flag with specified name, default value, and usage string.
+// The argument p points to an int64 variable in which to store the value of the flag.
+func Int64Var(p *int64, names []string, value int64, usage string) {
+ CommandLine.Var(newInt64Value(value, p), names, usage)
+}
+
+// Int64 defines an int64 flag with specified name, default value, and usage string.
+// The return value is the address of an int64 variable that stores the value of the flag.
+func (fs *FlagSet) Int64(names []string, value int64, usage string) *int64 {
+ p := new(int64)
+ fs.Int64Var(p, names, value, usage)
+ return p
+}
+
+// Int64 defines an int64 flag with specified name, default value, and usage string.
+// The return value is the address of an int64 variable that stores the value of the flag.
+func Int64(names []string, value int64, usage string) *int64 {
+ return CommandLine.Int64(names, value, usage)
+}
+
+// UintVar defines a uint flag with specified name, default value, and usage string.
+// The argument p points to a uint variable in which to store the value of the flag.
+func (fs *FlagSet) UintVar(p *uint, names []string, value uint, usage string) {
+ fs.Var(newUintValue(value, p), names, usage)
+}
+
+// UintVar defines a uint flag with specified name, default value, and usage string.
+// The argument p points to a uint variable in which to store the value of the flag.
+func UintVar(p *uint, names []string, value uint, usage string) {
+ CommandLine.Var(newUintValue(value, p), names, usage)
+}
+
+// Uint defines a uint flag with specified name, default value, and usage string.
+// The return value is the address of a uint variable that stores the value of the flag.
+func (fs *FlagSet) Uint(names []string, value uint, usage string) *uint {
+ p := new(uint)
+ fs.UintVar(p, names, value, usage)
+ return p
+}
+
+// Uint defines a uint flag with specified name, default value, and usage string.
+// The return value is the address of a uint variable that stores the value of the flag.
+func Uint(names []string, value uint, usage string) *uint {
+ return CommandLine.Uint(names, value, usage)
+}
+
+// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
+// The argument p points to a uint64 variable in which to store the value of the flag.
+func (fs *FlagSet) Uint64Var(p *uint64, names []string, value uint64, usage string) {
+ fs.Var(newUint64Value(value, p), names, usage)
+}
+
+// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
+// The argument p points to a uint64 variable in which to store the value of the flag.
+func Uint64Var(p *uint64, names []string, value uint64, usage string) {
+ CommandLine.Var(newUint64Value(value, p), names, usage)
+}
+
+// Uint64 defines a uint64 flag with specified name, default value, and usage string.
+// The return value is the address of a uint64 variable that stores the value of the flag.
+func (fs *FlagSet) Uint64(names []string, value uint64, usage string) *uint64 {
+ p := new(uint64)
+ fs.Uint64Var(p, names, value, usage)
+ return p
+}
+
+// Uint64 defines a uint64 flag with specified name, default value, and usage string.
+// The return value is the address of a uint64 variable that stores the value of the flag.
+func Uint64(names []string, value uint64, usage string) *uint64 {
+ return CommandLine.Uint64(names, value, usage)
+}
+
+// Uint16Var defines a uint16 flag with specified name, default value, and usage string.
+// The argument p points to a uint16 variable in which to store the value of the flag.
+func (fs *FlagSet) Uint16Var(p *uint16, names []string, value uint16, usage string) {
+ fs.Var(newUint16Value(value, p), names, usage)
+}
+
+// Uint16Var defines a uint16 flag with specified name, default value, and usage string.
+// The argument p points to a uint16 variable in which to store the value of the flag.
+func Uint16Var(p *uint16, names []string, value uint16, usage string) {
+ CommandLine.Var(newUint16Value(value, p), names, usage)
+}
+
+// Uint16 defines a uint16 flag with specified name, default value, and usage string.
+// The return value is the address of a uint16 variable that stores the value of the flag.
+func (fs *FlagSet) Uint16(names []string, value uint16, usage string) *uint16 {
+ p := new(uint16)
+ fs.Uint16Var(p, names, value, usage)
+ return p
+}
+
+// Uint16 defines a uint16 flag with specified name, default value, and usage string.
+// The return value is the address of a uint16 variable that stores the value of the flag.
+func Uint16(names []string, value uint16, usage string) *uint16 {
+ return CommandLine.Uint16(names, value, usage)
+}
+
+// StringVar defines a string flag with specified name, default value, and usage string.
+// The argument p points to a string variable in which to store the value of the flag.
+func (fs *FlagSet) StringVar(p *string, names []string, value string, usage string) {
+ fs.Var(newStringValue(value, p), names, usage)
+}
+
+// StringVar defines a string flag with specified name, default value, and usage string.
+// The argument p points to a string variable in which to store the value of the flag.
+func StringVar(p *string, names []string, value string, usage string) {
+ CommandLine.Var(newStringValue(value, p), names, usage)
+}
+
+// String defines a string flag with specified name, default value, and usage string.
+// The return value is the address of a string variable that stores the value of the flag.
+func (fs *FlagSet) String(names []string, value string, usage string) *string {
+ p := new(string)
+ fs.StringVar(p, names, value, usage)
+ return p
+}
+
+// String defines a string flag with specified name, default value, and usage string.
+// The return value is the address of a string variable that stores the value of the flag.
+func String(names []string, value string, usage string) *string {
+ return CommandLine.String(names, value, usage)
+}
+
+// Float64Var defines a float64 flag with specified name, default value, and usage string.
+// The argument p points to a float64 variable in which to store the value of the flag.
+func (fs *FlagSet) Float64Var(p *float64, names []string, value float64, usage string) {
+ fs.Var(newFloat64Value(value, p), names, usage)
+}
+
+// Float64Var defines a float64 flag with specified name, default value, and usage string.
+// The argument p points to a float64 variable in which to store the value of the flag.
+func Float64Var(p *float64, names []string, value float64, usage string) {
+ CommandLine.Var(newFloat64Value(value, p), names, usage)
+}
+
+// Float64 defines a float64 flag with specified name, default value, and usage string.
+// The return value is the address of a float64 variable that stores the value of the flag.
+func (fs *FlagSet) Float64(names []string, value float64, usage string) *float64 {
+ p := new(float64)
+ fs.Float64Var(p, names, value, usage)
+ return p
+}
+
+// Float64 defines a float64 flag with specified name, default value, and usage string.
+// The return value is the address of a float64 variable that stores the value of the flag.
+func Float64(names []string, value float64, usage string) *float64 {
+ return CommandLine.Float64(names, value, usage)
+}
+
+// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
+// The argument p points to a time.Duration variable in which to store the value of the flag.
+func (fs *FlagSet) DurationVar(p *time.Duration, names []string, value time.Duration, usage string) {
+ fs.Var(newDurationValue(value, p), names, usage)
+}
+
+// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
+// The argument p points to a time.Duration variable in which to store the value of the flag.
+func DurationVar(p *time.Duration, names []string, value time.Duration, usage string) {
+ CommandLine.Var(newDurationValue(value, p), names, usage)
+}
+
+// Duration defines a time.Duration flag with specified name, default value, and usage string.
+// The return value is the address of a time.Duration variable that stores the value of the flag.
+func (fs *FlagSet) Duration(names []string, value time.Duration, usage string) *time.Duration {
+ p := new(time.Duration)
+ fs.DurationVar(p, names, value, usage)
+ return p
+}
+
+// Duration defines a time.Duration flag with specified name, default value, and usage string.
+// The return value is the address of a time.Duration variable that stores the value of the flag.
+func Duration(names []string, value time.Duration, usage string) *time.Duration {
+ return CommandLine.Duration(names, value, usage)
+}
+
+// Var defines a flag with the specified name and usage string. The type and
+// value of the flag are represented by the first argument, of type Value, which
+// typically holds a user-defined implementation of Value. For instance, the
+// caller could create a flag that turns a comma-separated string into a slice
+// of strings by giving the slice the methods of Value; in particular, Set would
+// decompose the comma-separated string into the slice.
+func (fs *FlagSet) Var(value Value, names []string, usage string) {
+ // Remember the default value as a string; it won't change.
+ flag := &Flag{names, usage, value, value.String()}
+ for _, name := range names {
+ name = strings.TrimPrefix(name, "#")
+ _, alreadythere := fs.formal[name]
+ if alreadythere {
+ var msg string
+ if fs.name == "" {
+ msg = fmt.Sprintf("flag redefined: %s", name)
+ } else {
+ msg = fmt.Sprintf("%s flag redefined: %s", fs.name, name)
+ }
+ fmt.Fprintln(fs.Out(), msg)
+ panic(msg) // Happens only if flags are declared with identical names
+ }
+ if fs.formal == nil {
+ fs.formal = make(map[string]*Flag)
+ }
+ fs.formal[name] = flag
+ }
+}
+
+// Var defines a flag with the specified name and usage string. The type and
+// value of the flag are represented by the first argument, of type Value, which
+// typically holds a user-defined implementation of Value. For instance, the
+// caller could create a flag that turns a comma-separated string into a slice
+// of strings by giving the slice the methods of Value; in particular, Set would
+// decompose the comma-separated string into the slice.
+func Var(value Value, names []string, usage string) {
+ CommandLine.Var(value, names, usage)
+}
+
+// failf prints to standard error a formatted error and usage message and
+// returns the error.
+func (fs *FlagSet) failf(format string, a ...interface{}) error {
+ err := fmt.Errorf(format, a...)
+ fmt.Fprintln(fs.Out(), err)
+ if os.Args[0] == fs.name {
+ fmt.Fprintf(fs.Out(), "See '%s --help'.\n", os.Args[0])
+ } else {
+ fmt.Fprintf(fs.Out(), "See '%s %s --help'.\n", os.Args[0], fs.name)
+ }
+ return err
+}
+
+// usage calls the Usage method for the flag set, or the usage function if
+// the flag set is CommandLine.
+func (fs *FlagSet) usage() {
+ if fs == CommandLine {
+ Usage()
+ } else if fs.Usage == nil {
+ defaultUsage(fs)
+ } else {
+ fs.Usage()
+ }
+}
+
+func trimQuotes(str string) string {
+ if len(str) == 0 {
+ return str
+ }
+ type quote struct {
+ start, end byte
+ }
+
+ // All valid quote types.
+ quotes := []quote{
+ // Double quotes
+ {
+ start: '"',
+ end: '"',
+ },
+
+ // Single quotes
+ {
+ start: '\'',
+ end: '\'',
+ },
+ }
+
+ for _, quote := range quotes {
+ // Only strip if outermost match.
+ if str[0] == quote.start && str[len(str)-1] == quote.end {
+ str = str[1 : len(str)-1]
+ break
+ }
+ }
+
+ return str
+}
+
+// parseOne parses one flag. It reports whether a flag was seen.
+func (fs *FlagSet) parseOne() (bool, string, error) {
+ if len(fs.args) == 0 {
+ return false, "", nil
+ }
+ s := fs.args[0]
+ if len(s) == 0 || s[0] != '-' || len(s) == 1 {
+ return false, "", nil
+ }
+ if s[1] == '-' && len(s) == 2 { // "--" terminates the flags
+ fs.args = fs.args[1:]
+ return false, "", nil
+ }
+ name := s[1:]
+ if len(name) == 0 || name[0] == '=' {
+ return false, "", fs.failf("bad flag syntax: %s", s)
+ }
+
+ // it's a flag. does it have an argument?
+ fs.args = fs.args[1:]
+ hasValue := false
+ value := ""
+ if i := strings.Index(name, "="); i != -1 {
+ value = trimQuotes(name[i+1:])
+ hasValue = true
+ name = name[:i]
+ }
+
+ m := fs.formal
+ flag, alreadythere := m[name] // BUG
+ if !alreadythere {
+ if name == "-help" || name == "help" || name == "h" { // special case for nice help message.
+ fs.usage()
+ return false, "", ErrHelp
+ }
+ if len(name) > 0 && name[0] == '-' {
+ return false, "", fs.failf("flag provided but not defined: -%s", name)
+ }
+ return false, name, ErrRetry
+ }
+ if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
+ if hasValue {
+ if err := fv.Set(value); err != nil {
+ return false, "", fs.failf("invalid boolean value %q for -%s: %v", value, name, err)
+ }
+ } else {
+ fv.Set("true")
+ }
+ } else {
+ // It must have a value, which might be the next argument.
+ if !hasValue && len(fs.args) > 0 {
+ // value is the next arg
+ hasValue = true
+ value, fs.args = fs.args[0], fs.args[1:]
+ }
+ if !hasValue {
+ return false, "", fs.failf("flag needs an argument: -%s", name)
+ }
+ if err := flag.Value.Set(value); err != nil {
+ return false, "", fs.failf("invalid value %q for flag -%s: %v", value, name, err)
+ }
+ }
+ if fs.actual == nil {
+ fs.actual = make(map[string]*Flag)
+ }
+ fs.actual[name] = flag
+ for i, n := range flag.Names {
+ if n == fmt.Sprintf("#%s", name) {
+ replacement := ""
+ for j := i; j < len(flag.Names); j++ {
+ if flag.Names[j][0] != '#' {
+ replacement = flag.Names[j]
+ break
+ }
+ }
+ if replacement != "" {
+ fmt.Fprintf(fs.Out(), "Warning: '-%s' is deprecated, it will be replaced by '-%s' soon. See usage.\n", name, replacement)
+ } else {
+ fmt.Fprintf(fs.Out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name)
+ }
+ }
+ }
+ return true, "", nil
+}
+
+// Parse parses flag definitions from the argument list, which should not
+// include the command name. Must be called after all flags in the FlagSet
+// are defined and before flags are accessed by the program.
+// The return value will be ErrHelp if -help was set but not defined.
+func (fs *FlagSet) Parse(arguments []string) error {
+ fs.parsed = true
+ fs.args = arguments
+ for {
+ seen, name, err := fs.parseOne()
+ if seen {
+ continue
+ }
+ if err == nil {
+ break
+ }
+ if err == ErrRetry {
+ if len(name) > 1 {
+ err = nil
+ for _, letter := range strings.Split(name, "") {
+ fs.args = append([]string{"-" + letter}, fs.args...)
+ seen2, _, err2 := fs.parseOne()
+ if seen2 {
+ continue
+ }
+ if err2 != nil {
+ err = fs.failf("flag provided but not defined: -%s", name)
+ break
+ }
+ }
+ if err == nil {
+ continue
+ }
+ } else {
+ err = fs.failf("flag provided but not defined: -%s", name)
+ }
+ }
+ switch fs.errorHandling {
+ case ContinueOnError:
+ return err
+ case ExitOnError:
+ os.Exit(125)
+ case PanicOnError:
+ panic(err)
+ }
+ }
+ return nil
+}
+
+// ParseFlags is a utility function that adds a help flag if withHelp is true,
+// calls fs.Parse(args) and prints a relevant error message if there are
+// incorrect number of arguments. It returns error only if error handling is
+// set to ContinueOnError and parsing fails. If error handling is set to
+// ExitOnError, it's safe to ignore the return value.
+func (fs *FlagSet) ParseFlags(args []string, withHelp bool) error {
+ var help *bool
+ if withHelp {
+ help = fs.Bool([]string{"#help", "-help"}, false, "Print usage")
+ }
+ if err := fs.Parse(args); err != nil {
+ return err
+ }
+ if help != nil && *help {
+ fs.SetOutput(os.Stdout)
+ fs.Usage()
+ os.Exit(0)
+ }
+ if str := fs.CheckArgs(); str != "" {
+ fs.SetOutput(os.Stderr)
+ fs.ReportError(str, withHelp)
+ fs.ShortUsage()
+ os.Exit(1)
+ }
+ return nil
+}
+
+// ReportError is a utility method that prints a user-friendly message
+// containing the error that occurred during parsing and a suggestion to get help
+func (fs *FlagSet) ReportError(str string, withHelp bool) {
+ if withHelp {
+ if os.Args[0] == fs.Name() {
+ str += ".\nSee '" + os.Args[0] + " --help'"
+ } else {
+ str += ".\nSee '" + os.Args[0] + " " + fs.Name() + " --help'"
+ }
+ }
+ fmt.Fprintf(fs.Out(), "docker: %s.\n", str)
+}
+
+// Parsed reports whether fs.Parse has been called.
+func (fs *FlagSet) Parsed() bool {
+ return fs.parsed
+}
+
+// Parse parses the command-line flags from os.Args[1:]. Must be called
+// after all flags are defined and before flags are accessed by the program.
+func Parse() {
+ // Ignore errors; CommandLine is set for ExitOnError.
+ CommandLine.Parse(os.Args[1:])
+}
+
+// Parsed returns true if the command-line flags have been parsed.
+func Parsed() bool {
+ return CommandLine.Parsed()
+}
+
+// CommandLine is the default set of command-line flags, parsed from os.Args.
+// The top-level functions such as BoolVar, Arg, and on are wrappers for the
+// methods of CommandLine.
+var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
+
+// NewFlagSet returns a new, empty flag set with the specified name and
+// error handling property.
+func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
+ f := &FlagSet{
+ name: name,
+ errorHandling: errorHandling,
+ }
+ return f
+}
+
+// Init sets the name and error handling property for a flag set.
+// By default, the zero FlagSet uses an empty name and the
+// ContinueOnError error handling policy.
+func (fs *FlagSet) Init(name string, errorHandling ErrorHandling) {
+ fs.name = name
+ fs.errorHandling = errorHandling
+}
+
+type mergeVal struct {
+ Value
+ key string
+ fset *FlagSet
+}
+
+func (v mergeVal) Set(s string) error {
+ return v.fset.Set(v.key, s)
+}
+
+func (v mergeVal) IsBoolFlag() bool {
+ if b, ok := v.Value.(boolFlag); ok {
+ return b.IsBoolFlag()
+ }
+ return false
+}
+
+// Merge is an helper function that merges n FlagSets into a single dest FlagSet
+// In case of name collision between the flagsets it will apply
+// the destination FlagSet's errorHandling behaviour.
+func Merge(dest *FlagSet, flagsets ...*FlagSet) error {
+ for _, fset := range flagsets {
+ for k, f := range fset.formal {
+ if _, ok := dest.formal[k]; ok {
+ var err error
+ if fset.name == "" {
+ err = fmt.Errorf("flag redefined: %s", k)
+ } else {
+ err = fmt.Errorf("%s flag redefined: %s", fset.name, k)
+ }
+ fmt.Fprintln(fset.Out(), err.Error())
+ // Happens only if flags are declared with identical names
+ switch dest.errorHandling {
+ case ContinueOnError:
+ return err
+ case ExitOnError:
+ os.Exit(2)
+ case PanicOnError:
+ panic(err)
+ }
+ }
+ newF := *f
+ newF.Value = mergeVal{f.Value, k, fset}
+ dest.formal[k] = &newF
+ }
+ }
+ return nil
+}
+
+// IsEmpty reports if the FlagSet is actually empty.
+func (fs *FlagSet) IsEmpty() bool {
+ return len(fs.actual) == 0
+}
diff --git a/vendor/github.com/docker/docker/pkg/mflag/flag_test.go b/vendor/github.com/docker/docker/pkg/mflag/flag_test.go
new file mode 100644
index 000000000..85f32c8aa
--- /dev/null
+++ b/vendor/github.com/docker/docker/pkg/mflag/flag_test.go
@@ -0,0 +1,516 @@
+// Copyright 2014-2015 The Docker & 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 mflag
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+)
+
+// ResetForTesting clears all flag state and sets the usage function as directed.
+// After calling ResetForTesting, parse errors in flag handling will not
+// exit the program.
+func ResetForTesting(usage func()) {
+ CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
+ Usage = usage
+}
+func boolString(s string) string {
+ if s == "0" {
+ return "false"
+ }
+ return "true"
+}
+
+func TestEverything(t *testing.T) {
+ ResetForTesting(nil)
+ Bool([]string{"test_bool"}, false, "bool value")
+ Int([]string{"test_int"}, 0, "int value")
+ Int64([]string{"test_int64"}, 0, "int64 value")
+ Uint([]string{"test_uint"}, 0, "uint value")
+ Uint64([]string{"test_uint64"}, 0, "uint64 value")
+ String([]string{"test_string"}, "0", "string value")
+ Float64([]string{"test_float64"}, 0, "float64 value")
+ Duration([]string{"test_duration"}, 0, "time.Duration value")
+
+ m := make(map[string]*Flag)
+ desired := "0"
+ visitor := func(f *Flag) {
+ for _, name := range f.Names {
+ if len(name) > 5 && name[0:5] == "test_" {
+ m[name] = f
+ ok := false
+ switch {
+ case f.Value.String() == desired:
+ ok = true
+ case name == "test_bool" && f.Value.String() == boolString(desired):
+ ok = true
+ case name == "test_duration" && f.Value.String() == desired+"s":
+ ok = true
+ }
+ if !ok {
+ t.Error("Visit: bad value", f.Value.String(), "for", name)
+ }
+ }
+ }
+ }
+ VisitAll(visitor)
+ if len(m) != 8 {
+ t.Error("VisitAll misses some flags")
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ m = make(map[string]*Flag)
+ Visit(visitor)
+ if len(m) != 0 {
+ t.Errorf("Visit sees unset flags")
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ // Now set all flags
+ Set("test_bool", "true")
+ Set("test_int", "1")
+ Set("test_int64", "1")
+ Set("test_uint", "1")
+ Set("test_uint64", "1")
+ Set("test_string", "1")
+ Set("test_float64", "1")
+ Set("test_duration", "1s")
+ desired = "1"
+ Visit(visitor)
+ if len(m) != 8 {
+ t.Error("Visit fails after set")
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ // Now test they're visited in sort order.
+ var flagNames []string
+ Visit(func(f *Flag) {
+ for _, name := range f.Names {
+ flagNames = append(flagNames, name)
+ }
+ })
+ if !sort.StringsAreSorted(flagNames) {
+ t.Errorf("flag names not sorted: %v", flagNames)
+ }
+}
+
+func TestGet(t *testing.T) {
+ ResetForTesting(nil)
+ Bool([]string{"test_bool"}, true, "bool value")
+ Int([]string{"test_int"}, 1, "int value")
+ Int64([]string{"test_int64"}, 2, "int64 value")
+ Uint([]string{"test_uint"}, 3, "uint value")
+ Uint64([]string{"test_uint64"}, 4, "uint64 value")
+ String([]string{"test_string"}, "5", "string value")
+ Float64([]string{"test_float64"}, 6, "float64 value")
+ Duration([]string{"test_duration"}, 7, "time.Duration value")
+
+ visitor := func(f *Flag) {
+ for _, name := range f.Names {
+ if len(name) > 5 && name[0:5] == "test_" {
+ g, ok := f.Value.(Getter)
+ if !ok {
+ t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
+ return
+ }
+ switch name {
+ case "test_bool":
+ ok = g.Get() == true
+ case "test_int":
+ ok = g.Get() == int(1)
+ case "test_int64":
+ ok = g.Get() == int64(2)
+ case "test_uint":
+ ok = g.Get() == uint(3)
+ case "test_uint64":
+ ok = g.Get() == uint64(4)
+ case "test_string":
+ ok = g.Get() == "5"
+ case "test_float64":
+ ok = g.Get() == float64(6)
+ case "test_duration":
+ ok = g.Get() == time.Duration(7)
+ }
+ if !ok {
+ t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), name)
+ }
+ }
+ }
+ }
+ VisitAll(visitor)
+}
+
+func testParse(f *FlagSet, t *testing.T) {
+ if f.Parsed() {
+ t.Error("f.Parse() = true before Parse")
+ }
+ boolFlag := f.Bool([]string{"bool"}, false, "bool value")
+ bool2Flag := f.Bool([]string{"bool2"}, false, "bool2 value")
+ f.Bool([]string{"bool3"}, false, "bool3 value")
+ bool4Flag := f.Bool([]string{"bool4"}, false, "bool4 value")
+ intFlag := f.Int([]string{"-int"}, 0, "int value")
+ int64Flag := f.Int64([]string{"-int64"}, 0, "int64 value")
+ uintFlag := f.Uint([]string{"uint"}, 0, "uint value")
+ uint64Flag := f.Uint64([]string{"-uint64"}, 0, "uint64 value")
+ stringFlag := f.String([]string{"string"}, "0", "string value")
+ f.String([]string{"string2"}, "0", "string2 value")
+ singleQuoteFlag := f.String([]string{"squote"}, "", "single quoted value")
+ doubleQuoteFlag := f.String([]string{"dquote"}, "", "double quoted value")
+ mixedQuoteFlag := f.String([]string{"mquote"}, "", "mixed quoted value")
+ mixed2QuoteFlag := f.String([]string{"mquote2"}, "", "mixed2 quoted value")
+ nestedQuoteFlag := f.String([]string{"nquote"}, "", "nested quoted value")
+ nested2QuoteFlag := f.String([]string{"nquote2"}, "", "nested2 quoted value")
+ float64Flag := f.Float64([]string{"float64"}, 0, "float64 value")
+ durationFlag := f.Duration([]string{"duration"}, 5*time.Second, "time.Duration value")
+ extra := "one-extra-argument"
+ args := []string{
+ "-bool",
+ "-bool2=true",
+ "-bool4=false",
+ "--int", "22",
+ "--int64", "0x23",
+ "-uint", "24",
+ "--uint64", "25",
+ "-string", "hello",
+ "-squote='single'",
+ `-dquote="double"`,
+ `-mquote='mixed"`,
+ `-mquote2="mixed2'`,
+ `-nquote="'single nested'"`,
+ `-nquote2='"double nested"'`,
+ "-float64", "2718e28",
+ "-duration", "2m",
+ extra,
+ }
+ if err := f.Parse(args); err != nil {
+ t.Fatal(err)
+ }
+ if !f.Parsed() {
+ t.Error("f.Parse() = false after Parse")
+ }
+ if *boolFlag != true {
+ t.Error("bool flag should be true, is ", *boolFlag)
+ }
+ if *bool2Flag != true {
+ t.Error("bool2 flag should be true, is ", *bool2Flag)
+ }
+ if !f.IsSet("bool2") {
+ t.Error("bool2 should be marked as set")
+ }
+ if f.IsSet("bool3") {
+ t.Error("bool3 should not be marked as set")
+ }
+ if !f.IsSet("bool4") {
+ t.Error("bool4 should be marked as set")
+ }
+ if *bool4Flag != false {
+ t.Error("bool4 flag should be false, is ", *bool4Flag)
+ }
+ if *intFlag != 22 {
+ t.Error("int flag should be 22, is ", *intFlag)
+ }
+ if *int64Flag != 0x23 {
+ t.Error("int64 flag should be 0x23, is ", *int64Flag)
+ }
+ if *uintFlag != 24 {
+ t.Error("uint flag should be 24, is ", *uintFlag)
+ }
+ if *uint64Flag != 25 {
+ t.Error("uint64 flag should be 25, is ", *uint64Flag)
+ }
+ if *stringFlag != "hello" {
+ t.Error("string flag should be `hello`, is ", *stringFlag)
+ }
+ if !f.IsSet("string") {
+ t.Error("string flag should be marked as set")
+ }
+ if f.IsSet("string2") {
+ t.Error("string2 flag should not be marked as set")
+ }
+ if *singleQuoteFlag != "single" {
+ t.Error("single quote string flag should be `single`, is ", *singleQuoteFlag)
+ }
+ if *doubleQuoteFlag != "double" {
+ t.Error("double quote string flag should be `double`, is ", *doubleQuoteFlag)
+ }
+ if *mixedQuoteFlag != `'mixed"` {
+ t.Error("mixed quote string flag should be `'mixed\"`, is ", *mixedQuoteFlag)
+ }
+ if *mixed2QuoteFlag != `"mixed2'` {
+ t.Error("mixed2 quote string flag should be `\"mixed2'`, is ", *mixed2QuoteFlag)
+ }
+ if *nestedQuoteFlag != "'single nested'" {
+ t.Error("nested quote string flag should be `'single nested'`, is ", *nestedQuoteFlag)
+ }
+ if *nested2QuoteFlag != `"double nested"` {
+ t.Error("double quote string flag should be `\"double nested\"`, is ", *nested2QuoteFlag)
+ }
+ if *float64Flag != 2718e28 {
+ t.Error("float64 flag should be 2718e28, is ", *float64Flag)
+ }
+ if *durationFlag != 2*time.Minute {
+ t.Error("duration flag should be 2m, is ", *durationFlag)
+ }
+ if len(f.Args()) != 1 {
+ t.Error("expected one argument, got", len(f.Args()))
+ } else if f.Args()[0] != extra {
+ t.Errorf("expected argument %q got %q", extra, f.Args()[0])
+ }
+}
+
+func testPanic(f *FlagSet, t *testing.T) {
+ f.Int([]string{"-int"}, 0, "int value")
+ if f.Parsed() {
+ t.Error("f.Parse() = true before Parse")
+ }
+ args := []string{
+ "-int", "21",
+ }
+ f.Parse(args)
+}
+
+func TestParsePanic(t *testing.T) {
+ ResetForTesting(func() {})
+ testPanic(CommandLine, t)
+}
+
+func TestParse(t *testing.T) {
+ ResetForTesting(func() { t.Error("bad parse") })
+ testParse(CommandLine, t)
+}
+
+func TestFlagSetParse(t *testing.T) {
+ testParse(NewFlagSet("test", ContinueOnError), t)
+}
+
+// Declare a user-defined flag type.
+type flagVar []string
+
+func (f *flagVar) String() string {
+ return fmt.Sprint([]string(*f))
+}
+
+func (f *flagVar) Set(value string) error {
+ *f = append(*f, value)
+ return nil
+}
+
+func TestUserDefined(t *testing.T) {
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
+ var v flagVar
+ flags.Var(&v, []string{"v"}, "usage")
+ if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
+ t.Error(err)
+ }
+ if len(v) != 3 {
+ t.Fatal("expected 3 args; got ", len(v))
+ }
+ expect := "[1 2 3]"
+ if v.String() != expect {
+ t.Errorf("expected value %q got %q", expect, v.String())
+ }
+}
+
+// Declare a user-defined boolean flag type.
+type boolFlagVar struct {
+ count int
+}
+
+func (b *boolFlagVar) String() string {
+ return fmt.Sprintf("%d", b.count)
+}
+
+func (b *boolFlagVar) Set(value string) error {
+ if value == "true" {
+ b.count++
+ }
+ return nil
+}
+
+func (b *boolFlagVar) IsBoolFlag() bool {
+ return b.count < 4
+}
+
+func TestUserDefinedBool(t *testing.T) {
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
+ var b boolFlagVar
+ var err error
+ flags.Var(&b, []string{"b"}, "usage")
+ if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
+ if b.count < 4 {
+ t.Error(err)
+ }
+ }
+
+ if b.count != 4 {
+ t.Errorf("want: %d; got: %d", 4, b.count)
+ }
+
+ if err == nil {
+ t.Error("expected error; got none")
+ }
+}
+
+func TestSetOutput(t *testing.T) {
+ var flags FlagSet
+ var buf bytes.Buffer
+ flags.SetOutput(&buf)
+ flags.Init("test", ContinueOnError)
+ flags.Parse([]string{"-unknown"})
+ if out := buf.String(); !strings.Contains(out, "-unknown") {
+ t.Logf("expected output mentioning unknown; got %q", out)
+ }
+}
+
+// This tests that one can reset the flags. This still works but not well, and is
+// superseded by FlagSet.
+func TestChangingArgs(t *testing.T) {
+ ResetForTesting(func() { t.Fatal("bad parse") })
+ oldArgs := os.Args
+ defer func() { os.Args = oldArgs }()
+ os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
+ before := Bool([]string{"before"}, false, "")
+ if err := CommandLine.Parse(os.Args[1:]); err != nil {
+ t.Fatal(err)
+ }
+ cmd := Arg(0)
+ os.Args = Args()
+ after := Bool([]string{"after"}, false, "")
+ Parse()
+ args := Args()
+
+ if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
+ t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
+ }
+}
+
+// Test that -help invokes the usage message and returns ErrHelp.
+func TestHelp(t *testing.T) {
+ var helpCalled = false
+ fs := NewFlagSet("help test", ContinueOnError)
+ fs.Usage = func() { helpCalled = true }
+ var flag bool
+ fs.BoolVar(&flag, []string{"flag"}, false, "regular flag")
+ // Regular flag invocation should work
+ err := fs.Parse([]string{"-flag=true"})
+ if err != nil {
+ t.Fatal("expected no error; got ", err)
+ }
+ if !flag {
+ t.Error("flag was not set by -flag")
+ }
+ if helpCalled {
+ t.Error("help called for regular flag")
+ helpCalled = false // reset for next test
+ }
+ // Help flag should work as expected.
+ err = fs.Parse([]string{"-help"})
+ if err == nil {
+ t.Fatal("error expected")
+ }
+ if err != ErrHelp {
+ t.Fatal("expected ErrHelp; got ", err)
+ }
+ if !helpCalled {
+ t.Fatal("help was not called")
+ }
+ // If we define a help flag, that should override.
+ var help bool
+ fs.BoolVar(&help, []string{"help"}, false, "help flag")
+ helpCalled = false
+ err = fs.Parse([]string{"-help"})
+ if err != nil {
+ t.Fatal("expected no error for defined -help; got ", err)
+ }
+ if helpCalled {
+ t.Fatal("help was called; should not have been for defined help flag")
+ }
+}
+
+// Test the flag count functions.
+func TestFlagCounts(t *testing.T) {
+ fs := NewFlagSet("help test", ContinueOnError)
+ var flag bool
+ fs.BoolVar(&flag, []string{"flag1"}, false, "regular flag")
+ fs.BoolVar(&flag, []string{"#deprecated1"}, false, "regular flag")
+ fs.BoolVar(&flag, []string{"f", "flag2"}, false, "regular flag")
+ fs.BoolVar(&flag, []string{"#d", "#deprecated2"}, false, "regular flag")
+ fs.BoolVar(&flag, []string{"flag3"}, false, "regular flag")
+ fs.BoolVar(&flag, []string{"g", "#flag4", "-flag4"}, false, "regular flag")
+
+ if fs.FlagCount() != 6 {
+ t.Fatal("FlagCount wrong. ", fs.FlagCount())
+ }
+ if fs.FlagCountUndeprecated() != 4 {
+ t.Fatal("FlagCountUndeprecated wrong. ", fs.FlagCountUndeprecated())
+ }
+ if fs.NFlag() != 0 {
+ t.Fatal("NFlag wrong. ", fs.NFlag())
+ }
+ err := fs.Parse([]string{"-fd", "-g", "-flag4"})
+ if err != nil {
+ t.Fatal("expected no error for defined -help; got ", err)
+ }
+ if fs.NFlag() != 4 {
+ t.Fatal("NFlag wrong. ", fs.NFlag())
+ }
+}
+
+// Show up bug in sortFlags
+func TestSortFlags(t *testing.T) {
+ fs := NewFlagSet("help TestSortFlags", ContinueOnError)
+
+ var err error
+
+ var b bool
+ fs.BoolVar(&b, []string{"b", "-banana"}, false, "usage")
+
+ err = fs.Parse([]string{"--banana=true"})
+ if err != nil {
+ t.Fatal("expected no error; got ", err)
+ }
+
+ count := 0
+
+ fs.VisitAll(func(flag *Flag) {
+ count++
+ if flag == nil {
+ t.Fatal("VisitAll should not return a nil flag")
+ }
+ })
+ flagcount := fs.FlagCount()
+ if flagcount != count {
+ t.Fatalf("FlagCount (%d) != number (%d) of elements visited", flagcount, count)
+ }
+ // Make sure its idempotent
+ if flagcount != fs.FlagCount() {
+ t.Fatalf("FlagCount (%d) != fs.FlagCount() (%d) of elements visited", flagcount, fs.FlagCount())
+ }
+
+ count = 0
+ fs.Visit(func(flag *Flag) {
+ count++
+ if flag == nil {
+ t.Fatal("Visit should not return a nil flag")
+ }
+ })
+ nflag := fs.NFlag()
+ if nflag != count {
+ t.Fatalf("NFlag (%d) != number (%d) of elements visited", nflag, count)
+ }
+ if nflag != fs.NFlag() {
+ t.Fatalf("NFlag (%d) != fs.NFlag() (%d) of elements visited", nflag, fs.NFlag())
+ }
+}
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/MAINTAINERS b/vendor/github.com/opencontainers/runc/libcontainer/user/MAINTAINERS
new file mode 100644
index 000000000..edbe20066
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/user/MAINTAINERS
@@ -0,0 +1,2 @@
+Tianon Gravi (@tianon)
+Aleksa Sarai (@cyphar)
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go
new file mode 100644
index 000000000..6f8a982ff
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go
@@ -0,0 +1,108 @@
+package user
+
+import (
+ "errors"
+ "fmt"
+ "syscall"
+)
+
+var (
+ // The current operating system does not provide the required data for user lookups.
+ ErrUnsupported = errors.New("user lookup: operating system does not provide passwd-formatted data")
+)
+
+func lookupUser(filter func(u User) bool) (User, error) {
+ // Get operating system-specific passwd reader-closer.
+ passwd, err := GetPasswd()
+ if err != nil {
+ return User{}, err
+ }
+ defer passwd.Close()
+
+ // Get the users.
+ users, err := ParsePasswdFilter(passwd, filter)
+ if err != nil {
+ return User{}, err
+ }
+
+ // No user entries found.
+ if len(users) == 0 {
+ return User{}, fmt.Errorf("no matching entries in passwd file")
+ }
+
+ // Assume the first entry is the "correct" one.
+ return users[0], nil
+}
+
+// CurrentUser looks up the current user by their user id in /etc/passwd. If the
+// user cannot be found (or there is no /etc/passwd file on the filesystem),
+// then CurrentUser returns an error.
+func CurrentUser() (User, error) {
+ return LookupUid(syscall.Getuid())
+}
+
+// LookupUser looks up a user by their username in /etc/passwd. If the user
+// cannot be found (or there is no /etc/passwd file on the filesystem), then
+// LookupUser returns an error.
+func LookupUser(username string) (User, error) {
+ return lookupUser(func(u User) bool {
+ return u.Name == username
+ })
+}
+
+// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot
+// be found (or there is no /etc/passwd file on the filesystem), then LookupId
+// returns an error.
+func LookupUid(uid int) (User, error) {
+ return lookupUser(func(u User) bool {
+ return u.Uid == uid
+ })
+}
+
+func lookupGroup(filter func(g Group) bool) (Group, error) {
+ // Get operating system-specific group reader-closer.
+ group, err := GetGroup()
+ if err != nil {
+ return Group{}, err
+ }
+ defer group.Close()
+
+ // Get the users.
+ groups, err := ParseGroupFilter(group, filter)
+ if err != nil {
+ return Group{}, err
+ }
+
+ // No user entries found.
+ if len(groups) == 0 {
+ return Group{}, fmt.Errorf("no matching entries in group file")
+ }
+
+ // Assume the first entry is the "correct" one.
+ return groups[0], nil
+}
+
+// CurrentGroup looks up the current user's group by their primary group id's
+// entry in /etc/passwd. If the group cannot be found (or there is no
+// /etc/group file on the filesystem), then CurrentGroup returns an error.
+func CurrentGroup() (Group, error) {
+ return LookupGid(syscall.Getgid())
+}
+
+// LookupGroup looks up a group by its name in /etc/group. If the group cannot
+// be found (or there is no /etc/group file on the filesystem), then LookupGroup
+// returns an error.
+func LookupGroup(groupname string) (Group, error) {
+ return lookupGroup(func(g Group) bool {
+ return g.Name == groupname
+ })
+}
+
+// LookupGid looks up a group by its group id in /etc/group. If the group cannot
+// be found (or there is no /etc/group file on the filesystem), then LookupGid
+// returns an error.
+func LookupGid(gid int) (Group, error) {
+ return lookupGroup(func(g Group) bool {
+ return g.Gid == gid
+ })
+}
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
new file mode 100644
index 000000000..758b734c2
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
@@ -0,0 +1,30 @@
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package user
+
+import (
+ "io"
+ "os"
+)
+
+// Unix-specific path to the passwd and group formatted files.
+const (
+ unixPasswdPath = "/etc/passwd"
+ unixGroupPath = "/etc/group"
+)
+
+func GetPasswdPath() (string, error) {
+ return unixPasswdPath, nil
+}
+
+func GetPasswd() (io.ReadCloser, error) {
+ return os.Open(unixPasswdPath)
+}
+
+func GetGroupPath() (string, error) {
+ return unixGroupPath, nil
+}
+
+func GetGroup() (io.ReadCloser, error) {
+ return os.Open(unixGroupPath)
+}
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go
new file mode 100644
index 000000000..721794887
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go
@@ -0,0 +1,21 @@
+// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+
+package user
+
+import "io"
+
+func GetPasswdPath() (string, error) {
+ return "", ErrUnsupported
+}
+
+func GetPasswd() (io.ReadCloser, error) {
+ return nil, ErrUnsupported
+}
+
+func GetGroupPath() (string, error) {
+ return "", ErrUnsupported
+}
+
+func GetGroup() (io.ReadCloser, error) {
+ return nil, ErrUnsupported
+}
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go
new file mode 100644
index 000000000..e6375ea4d
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go
@@ -0,0 +1,418 @@
+package user
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+)
+
+const (
+ minId = 0
+ maxId = 1<<31 - 1 //for 32-bit systems compatibility
+)
+
+var (
+ ErrRange = fmt.Errorf("Uids and gids must be in range %d-%d", minId, maxId)
+)
+
+type User struct {
+ Name string
+ Pass string
+ Uid int
+ Gid int
+ Gecos string
+ Home string
+ Shell string
+}
+
+type Group struct {
+ Name string
+ Pass string
+ Gid int
+ List []string
+}
+
+func parseLine(line string, v ...interface{}) {
+ if line == "" {
+ return
+ }
+
+ parts := strings.Split(line, ":")
+ for i, p := range parts {
+ if len(v) <= i {
+ // if we have more "parts" than we have places to put them, bail for great "tolerance" of naughty configuration files
+ break
+ }
+
+ switch e := v[i].(type) {
+ case *string:
+ // "root", "adm", "/bin/bash"
+ *e = p
+ case *int:
+ // "0", "4", "1000"
+ // ignore string to int conversion errors, for great "tolerance" of naughty configuration files
+ *e, _ = strconv.Atoi(p)
+ case *[]string:
+ // "", "root", "root,adm,daemon"
+ if p != "" {
+ *e = strings.Split(p, ",")
+ } else {
+ *e = []string{}
+ }
+ default:
+ // panic, because this is a programming/logic error, not a runtime one
+ panic("parseLine expects only pointers! argument " + strconv.Itoa(i) + " is not a pointer!")
+ }
+ }
+}
+
+func ParsePasswdFile(path string) ([]User, error) {
+ passwd, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer passwd.Close()
+ return ParsePasswd(passwd)
+}
+
+func ParsePasswd(passwd io.Reader) ([]User, error) {
+ return ParsePasswdFilter(passwd, nil)
+}
+
+func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) {
+ passwd, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer passwd.Close()
+ return ParsePasswdFilter(passwd, filter)
+}
+
+func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
+ if r == nil {
+ return nil, fmt.Errorf("nil source for passwd-formatted data")
+ }
+
+ var (
+ s = bufio.NewScanner(r)
+ out = []User{}
+ )
+
+ for s.Scan() {
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+
+ text := strings.TrimSpace(s.Text())
+ if text == "" {
+ continue
+ }
+
+ // see: man 5 passwd
+ // name:password:UID:GID:GECOS:directory:shell
+ // Name:Pass:Uid:Gid:Gecos:Home:Shell
+ // root:x:0:0:root:/root:/bin/bash
+ // adm:x:3:4:adm:/var/adm:/bin/false
+ p := User{}
+ parseLine(
+ text,
+ &p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell,
+ )
+
+ if filter == nil || filter(p) {
+ out = append(out, p)
+ }
+ }
+
+ return out, nil
+}
+
+func ParseGroupFile(path string) ([]Group, error) {
+ group, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer group.Close()
+ return ParseGroup(group)
+}
+
+func ParseGroup(group io.Reader) ([]Group, error) {
+ return ParseGroupFilter(group, nil)
+}
+
+func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) {
+ group, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer group.Close()
+ return ParseGroupFilter(group, filter)
+}
+
+func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
+ if r == nil {
+ return nil, fmt.Errorf("nil source for group-formatted data")
+ }
+
+ var (
+ s = bufio.NewScanner(r)
+ out = []Group{}
+ )
+
+ for s.Scan() {
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+
+ text := s.Text()
+ if text == "" {
+ continue
+ }
+
+ // see: man 5 group
+ // group_name:password:GID:user_list
+ // Name:Pass:Gid:List
+ // root:x:0:root
+ // adm:x:4:root,adm,daemon
+ p := Group{}
+ parseLine(
+ text,
+ &p.Name, &p.Pass, &p.Gid, &p.List,
+ )
+
+ if filter == nil || filter(p) {
+ out = append(out, p)
+ }
+ }
+
+ return out, nil
+}
+
+type ExecUser struct {
+ Uid, Gid int
+ Sgids []int
+ Home string
+}
+
+// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the
+// given file paths and uses that data as the arguments to GetExecUser. If the
+// files cannot be opened for any reason, the error is ignored and a nil
+// io.Reader is passed instead.
+func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
+ passwd, err := os.Open(passwdPath)
+ if err != nil {
+ passwd = nil
+ } else {
+ defer passwd.Close()
+ }
+
+ group, err := os.Open(groupPath)
+ if err != nil {
+ group = nil
+ } else {
+ defer group.Close()
+ }
+
+ return GetExecUser(userSpec, defaults, passwd, group)
+}
+
+// GetExecUser parses a user specification string (using the passwd and group
+// readers as sources for /etc/passwd and /etc/group data, respectively). In
+// the case of blank fields or missing data from the sources, the values in
+// defaults is used.
+//
+// GetExecUser will return an error if a user or group literal could not be
+// found in any entry in passwd and group respectively.
+//
+// Examples of valid user specifications are:
+// * ""
+// * "user"
+// * "uid"
+// * "user:group"
+// * "uid:gid
+// * "user:gid"
+// * "uid:group"
+func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) {
+ var (
+ userArg, groupArg string
+ name string
+ )
+
+ if defaults == nil {
+ defaults = new(ExecUser)
+ }
+
+ // Copy over defaults.
+ user := &ExecUser{
+ Uid: defaults.Uid,
+ Gid: defaults.Gid,
+ Sgids: defaults.Sgids,
+ Home: defaults.Home,
+ }
+
+ // Sgids slice *cannot* be nil.
+ if user.Sgids == nil {
+ user.Sgids = []int{}
+ }
+
+ // allow for userArg to have either "user" syntax, or optionally "user:group" syntax
+ parseLine(userSpec, &userArg, &groupArg)
+
+ users, err := ParsePasswdFilter(passwd, func(u User) bool {
+ if userArg == "" {
+ return u.Uid == user.Uid
+ }
+ return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
+ })
+ if err != nil && passwd != nil {
+ if userArg == "" {
+ userArg = strconv.Itoa(user.Uid)
+ }
+ return nil, fmt.Errorf("Unable to find user %v: %v", userArg, err)
+ }
+
+ haveUser := users != nil && len(users) > 0
+ if haveUser {
+ // if we found any user entries that matched our filter, let's take the first one as "correct"
+ name = users[0].Name
+ user.Uid = users[0].Uid
+ user.Gid = users[0].Gid
+ user.Home = users[0].Home
+ } else if userArg != "" {
+ // we asked for a user but didn't find them... let's check to see if we wanted a numeric user
+ user.Uid, err = strconv.Atoi(userArg)
+ if err != nil {
+ // not numeric - we have to bail
+ return nil, fmt.Errorf("Unable to find user %v", userArg)
+ }
+
+ // Must be inside valid uid range.
+ if user.Uid < minId || user.Uid > maxId {
+ return nil, ErrRange
+ }
+
+ // if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
+ }
+
+ if groupArg != "" || name != "" {
+ groups, err := ParseGroupFilter(group, func(g Group) bool {
+ // Explicit group format takes precedence.
+ if groupArg != "" {
+ return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
+ }
+
+ // Check if user is a member.
+ for _, u := range g.List {
+ if u == name {
+ return true
+ }
+ }
+
+ return false
+ })
+ if err != nil && group != nil {
+ return nil, fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
+ }
+
+ haveGroup := groups != nil && len(groups) > 0
+ if groupArg != "" {
+ if haveGroup {
+ // if we found any group entries that matched our filter, let's take the first one as "correct"
+ user.Gid = groups[0].Gid
+ } else {
+ // we asked for a group but didn't find id... let's check to see if we wanted a numeric group
+ user.Gid, err = strconv.Atoi(groupArg)
+ if err != nil {
+ // not numeric - we have to bail
+ return nil, fmt.Errorf("Unable to find group %v", groupArg)
+ }
+
+ // Ensure gid is inside gid range.
+ if user.Gid < minId || user.Gid > maxId {
+ return nil, ErrRange
+ }
+
+ // if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
+ }
+ } else if haveGroup {
+ // If implicit group format, fill supplementary gids.
+ user.Sgids = make([]int, len(groups))
+ for i, group := range groups {
+ user.Sgids[i] = group.Gid
+ }
+ }
+ }
+
+ return user, nil
+}
+
+// GetAdditionalGroups looks up a list of groups by name or group id
+// against the given /etc/group formatted data. If a group name cannot
+// be found, an error will be returned. If a group id cannot be found,
+// or the given group data is nil, the id will be returned as-is
+// provided it is in the legal range.
+func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) {
+ var groups = []Group{}
+ if group != nil {
+ var err error
+ groups, err = ParseGroupFilter(group, func(g Group) bool {
+ for _, ag := range additionalGroups {
+ if g.Name == ag || strconv.Itoa(g.Gid) == ag {
+ return true
+ }
+ }
+ return false
+ })
+ if err != nil {
+ return nil, fmt.Errorf("Unable to find additional groups %v: %v", additionalGroups, err)
+ }
+ }
+
+ gidMap := make(map[int]struct{})
+ for _, ag := range additionalGroups {
+ var found bool
+ for _, g := range groups {
+ // if we found a matched group either by name or gid, take the
+ // first matched as correct
+ if g.Name == ag || strconv.Itoa(g.Gid) == ag {
+ if _, ok := gidMap[g.Gid]; !ok {
+ gidMap[g.Gid] = struct{}{}
+ found = true
+ break
+ }
+ }
+ }
+ // we asked for a group but didn't find it. let's check to see
+ // if we wanted a numeric group
+ if !found {
+ gid, err := strconv.Atoi(ag)
+ if err != nil {
+ return nil, fmt.Errorf("Unable to find group %s", ag)
+ }
+ // Ensure gid is inside gid range.
+ if gid < minId || gid > maxId {
+ return nil, ErrRange
+ }
+ gidMap[gid] = struct{}{}
+ }
+ }
+ gids := []int{}
+ for gid := range gidMap {
+ gids = append(gids, gid)
+ }
+ return gids, nil
+}
+
+// GetAdditionalGroupsPath is a wrapper around GetAdditionalGroups
+// that opens the groupPath given and gives it as an argument to
+// GetAdditionalGroups.
+func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) {
+ group, err := os.Open(groupPath)
+ if err == nil {
+ defer group.Close()
+ }
+ return GetAdditionalGroups(additionalGroups, group)
+}
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user_test.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user_test.go
new file mode 100644
index 000000000..53b2289bf
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user_test.go
@@ -0,0 +1,472 @@
+package user
+
+import (
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestUserParseLine(t *testing.T) {
+ var (
+ a, b string
+ c []string
+ d int
+ )
+
+ parseLine("", &a, &b)
+ if a != "" || b != "" {
+ t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
+ }
+
+ parseLine("a", &a, &b)
+ if a != "a" || b != "" {
+ t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
+ }
+
+ parseLine("bad boys:corny cows", &a, &b)
+ if a != "bad boys" || b != "corny cows" {
+ t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
+ }
+
+ parseLine("", &c)
+ if len(c) != 0 {
+ t.Fatalf("c should be empty (%#v)", c)
+ }
+
+ parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c)
+ if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
+ t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
+ }
+
+ parseLine("::::::::::", &a, &b, &c)
+ if a != "" || b != "" || len(c) != 0 {
+ t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
+ }
+
+ parseLine("not a number", &d)
+ if d != 0 {
+ t.Fatalf("d should be 0 (%v)", d)
+ }
+
+ parseLine("b:12:c", &a, &d, &b)
+ if a != "b" || b != "c" || d != 12 {
+ t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
+ }
+}
+
+func TestUserParsePasswd(t *testing.T) {
+ users, err := ParsePasswdFilter(strings.NewReader(`
+root:x:0:0:root:/root:/bin/bash
+adm:x:3:4:adm:/var/adm:/bin/false
+this is just some garbage data
+`), nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+ if len(users) != 3 {
+ t.Fatalf("Expected 3 users, got %v", len(users))
+ }
+ if users[0].Uid != 0 || users[0].Name != "root" {
+ t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name)
+ }
+ if users[1].Uid != 3 || users[1].Name != "adm" {
+ t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name)
+ }
+}
+
+func TestUserParseGroup(t *testing.T) {
+ groups, err := ParseGroupFilter(strings.NewReader(`
+root:x:0:root
+adm:x:4:root,adm,daemon
+this is just some garbage data
+`), nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+ if len(groups) != 3 {
+ t.Fatalf("Expected 3 groups, got %v", len(groups))
+ }
+ if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 {
+ t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List))
+ }
+ if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 {
+ t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
+ }
+}
+
+func TestValidGetExecUser(t *testing.T) {
+ const passwdContent = `
+root:x:0:0:root user:/root:/bin/bash
+adm:x:42:43:adm:/var/adm:/bin/false
+this is just some garbage data
+`
+ const groupContent = `
+root:x:0:root
+adm:x:43:
+grp:x:1234:root,adm
+this is just some garbage data
+`
+ defaultExecUser := ExecUser{
+ Uid: 8888,
+ Gid: 8888,
+ Sgids: []int{8888},
+ Home: "/8888",
+ }
+
+ tests := []struct {
+ ref string
+ expected ExecUser
+ }{
+ {
+ ref: "root",
+ expected: ExecUser{
+ Uid: 0,
+ Gid: 0,
+ Sgids: []int{0, 1234},
+ Home: "/root",
+ },
+ },
+ {
+ ref: "adm",
+ expected: ExecUser{
+ Uid: 42,
+ Gid: 43,
+ Sgids: []int{1234},
+ Home: "/var/adm",
+ },
+ },
+ {
+ ref: "root:adm",
+ expected: ExecUser{
+ Uid: 0,
+ Gid: 43,
+ Sgids: defaultExecUser.Sgids,
+ Home: "/root",
+ },
+ },
+ {
+ ref: "adm:1234",
+ expected: ExecUser{
+ Uid: 42,
+ Gid: 1234,
+ Sgids: defaultExecUser.Sgids,
+ Home: "/var/adm",
+ },
+ },
+ {
+ ref: "42:1234",
+ expected: ExecUser{
+ Uid: 42,
+ Gid: 1234,
+ Sgids: defaultExecUser.Sgids,
+ Home: "/var/adm",
+ },
+ },
+ {
+ ref: "1337:1234",
+ expected: ExecUser{
+ Uid: 1337,
+ Gid: 1234,
+ Sgids: defaultExecUser.Sgids,
+ Home: defaultExecUser.Home,
+ },
+ },
+ {
+ ref: "1337",
+ expected: ExecUser{
+ Uid: 1337,
+ Gid: defaultExecUser.Gid,
+ Sgids: defaultExecUser.Sgids,
+ Home: defaultExecUser.Home,
+ },
+ },
+ {
+ ref: "",
+ expected: ExecUser{
+ Uid: defaultExecUser.Uid,
+ Gid: defaultExecUser.Gid,
+ Sgids: defaultExecUser.Sgids,
+ Home: defaultExecUser.Home,
+ },
+ },
+ }
+
+ for _, test := range tests {
+ passwd := strings.NewReader(passwdContent)
+ group := strings.NewReader(groupContent)
+
+ execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group)
+ if err != nil {
+ t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
+ t.Fail()
+ continue
+ }
+
+ if !reflect.DeepEqual(test.expected, *execUser) {
+ t.Logf("got: %#v", execUser)
+ t.Logf("expected: %#v", test.expected)
+ t.Fail()
+ continue
+ }
+ }
+}
+
+func TestInvalidGetExecUser(t *testing.T) {
+ const passwdContent = `
+root:x:0:0:root user:/root:/bin/bash
+adm:x:42:43:adm:/var/adm:/bin/false
+this is just some garbage data
+`
+ const groupContent = `
+root:x:0:root
+adm:x:43:
+grp:x:1234:root,adm
+this is just some garbage data
+`
+
+ tests := []string{
+ // No such user/group.
+ "notuser",
+ "notuser:notgroup",
+ "root:notgroup",
+ "notuser:adm",
+ "8888:notgroup",
+ "notuser:8888",
+
+ // Invalid user/group values.
+ "-1:0",
+ "0:-3",
+ "-5:-2",
+ }
+
+ for _, test := range tests {
+ passwd := strings.NewReader(passwdContent)
+ group := strings.NewReader(groupContent)
+
+ execUser, err := GetExecUser(test, nil, passwd, group)
+ if err == nil {
+ t.Logf("got unexpected success when parsing '%s': %#v", test, execUser)
+ t.Fail()
+ continue
+ }
+ }
+}
+
+func TestGetExecUserNilSources(t *testing.T) {
+ const passwdContent = `
+root:x:0:0:root user:/root:/bin/bash
+adm:x:42:43:adm:/var/adm:/bin/false
+this is just some garbage data
+`
+ const groupContent = `
+root:x:0:root
+adm:x:43:
+grp:x:1234:root,adm
+this is just some garbage data
+`
+
+ defaultExecUser := ExecUser{
+ Uid: 8888,
+ Gid: 8888,
+ Sgids: []int{8888},
+ Home: "/8888",
+ }
+
+ tests := []struct {
+ ref string
+ passwd, group bool
+ expected ExecUser
+ }{
+ {
+ ref: "",
+ passwd: false,
+ group: false,
+ expected: ExecUser{
+ Uid: 8888,
+ Gid: 8888,
+ Sgids: []int{8888},
+ Home: "/8888",
+ },
+ },
+ {
+ ref: "root",
+ passwd: true,
+ group: false,
+ expected: ExecUser{
+ Uid: 0,
+ Gid: 0,
+ Sgids: []int{8888},
+ Home: "/root",
+ },
+ },
+ {
+ ref: "0",
+ passwd: false,
+ group: false,
+ expected: ExecUser{
+ Uid: 0,
+ Gid: 8888,
+ Sgids: []int{8888},
+ Home: "/8888",
+ },
+ },
+ {
+ ref: "0:0",
+ passwd: false,
+ group: false,
+ expected: ExecUser{
+ Uid: 0,
+ Gid: 0,
+ Sgids: []int{8888},
+ Home: "/8888",
+ },
+ },
+ }
+
+ for _, test := range tests {
+ var passwd, group io.Reader
+
+ if test.passwd {
+ passwd = strings.NewReader(passwdContent)
+ }
+
+ if test.group {
+ group = strings.NewReader(groupContent)
+ }
+
+ execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group)
+ if err != nil {
+ t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
+ t.Fail()
+ continue
+ }
+
+ if !reflect.DeepEqual(test.expected, *execUser) {
+ t.Logf("got: %#v", execUser)
+ t.Logf("expected: %#v", test.expected)
+ t.Fail()
+ continue
+ }
+ }
+}
+
+func TestGetAdditionalGroups(t *testing.T) {
+ const groupContent = `
+root:x:0:root
+adm:x:43:
+grp:x:1234:root,adm
+adm:x:4343:root,adm-duplicate
+this is just some garbage data
+`
+ tests := []struct {
+ groups []string
+ expected []int
+ hasError bool
+ }{
+ {
+ // empty group
+ groups: []string{},
+ expected: []int{},
+ },
+ {
+ // single group
+ groups: []string{"adm"},
+ expected: []int{43},
+ },
+ {
+ // multiple groups
+ groups: []string{"adm", "grp"},
+ expected: []int{43, 1234},
+ },
+ {
+ // invalid group
+ groups: []string{"adm", "grp", "not-exist"},
+ expected: nil,
+ hasError: true,
+ },
+ {
+ // group with numeric id
+ groups: []string{"43"},
+ expected: []int{43},
+ },
+ {
+ // group with unknown numeric id
+ groups: []string{"adm", "10001"},
+ expected: []int{43, 10001},
+ },
+ {
+ // groups specified twice with numeric and name
+ groups: []string{"adm", "43"},
+ expected: []int{43},
+ },
+ {
+ // groups with too small id
+ groups: []string{"-1"},
+ expected: nil,
+ hasError: true,
+ },
+ {
+ // groups with too large id
+ groups: []string{strconv.Itoa(1 << 31)},
+ expected: nil,
+ hasError: true,
+ },
+ }
+
+ for _, test := range tests {
+ group := strings.NewReader(groupContent)
+
+ gids, err := GetAdditionalGroups(test.groups, group)
+ if test.hasError && err == nil {
+ t.Errorf("Parse(%#v) expects error but has none", test)
+ continue
+ }
+ if !test.hasError && err != nil {
+ t.Errorf("Parse(%#v) has error %v", test, err)
+ continue
+ }
+ sort.Sort(sort.IntSlice(gids))
+ if !reflect.DeepEqual(gids, test.expected) {
+ t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups)
+ }
+ }
+}
+
+func TestGetAdditionalGroupsNumeric(t *testing.T) {
+ tests := []struct {
+ groups []string
+ expected []int
+ hasError bool
+ }{
+ {
+ // numeric groups only
+ groups: []string{"1234", "5678"},
+ expected: []int{1234, 5678},
+ },
+ {
+ // numeric and alphabetic
+ groups: []string{"1234", "fake"},
+ expected: nil,
+ hasError: true,
+ },
+ }
+
+ for _, test := range tests {
+ gids, err := GetAdditionalGroups(test.groups, nil)
+ if test.hasError && err == nil {
+ t.Errorf("Parse(%#v) expects error but has none", test)
+ continue
+ }
+ if !test.hasError && err != nil {
+ t.Errorf("Parse(%#v) has error %v", test, err)
+ continue
+ }
+ sort.Sort(sort.IntSlice(gids))
+ if !reflect.DeepEqual(gids, test.expected) {
+ t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups)
+ }
+ }
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/datapath.go b/vendor/github.com/weaveworks/go-odp/odp/datapath.go
new file mode 100644
index 000000000..90f131491
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/datapath.go
@@ -0,0 +1,172 @@
+package odp
+
+import (
+ "fmt"
+ "syscall"
+)
+
+// Datapaths are identified by the ifindex of their netdev.
+type DatapathID int32
+
+type datapathInfo struct {
+ ifindex DatapathID
+ name string
+}
+
+func (dpif *Dpif) parseDatapathInfo(msg *NlMsgParser) (res datapathInfo, err error) {
+ _, ovshdr, err := dpif.checkNlMsgHeaders(msg, DATAPATH, OVS_DP_CMD_NEW)
+ if err != nil {
+ return
+ }
+
+ res.ifindex = ovshdr.datapathID()
+ attrs, err := msg.TakeAttrs()
+ if err != nil {
+ return
+ }
+
+ res.name, err = attrs.GetString(OVS_DP_ATTR_NAME)
+ return
+}
+
+type DatapathHandle struct {
+ dpif *Dpif
+ ifindex DatapathID
+}
+
+func (dp DatapathHandle) ID() DatapathID {
+ return dp.ifindex
+}
+
+func (dp DatapathHandle) Reopen() (DatapathHandle, error) {
+ dpif, err := dp.dpif.Reopen()
+ return DatapathHandle{dpif: dpif, ifindex: dp.ifindex}, err
+}
+
+func (dpif *Dpif) CreateDatapath(name string) (DatapathHandle, error) {
+ var features uint32 = OVS_DP_F_UNALIGNED | OVS_DP_F_VPORT_PIDS
+
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[DATAPATH].id)
+ req.PutGenlMsghdr(OVS_DP_CMD_NEW, OVS_DATAPATH_VERSION)
+ req.putOvsHeader(0)
+ req.PutStringAttr(OVS_DP_ATTR_NAME, name)
+ req.PutUint32Attr(OVS_DP_ATTR_UPCALL_PID, 0)
+ req.PutUint32Attr(OVS_DP_ATTR_USER_FEATURES, features)
+
+ resp, err := dpif.sock.Request(req)
+ if err != nil {
+ return DatapathHandle{}, err
+ }
+
+ dpi, err := dpif.parseDatapathInfo(resp)
+ if err != nil {
+ return DatapathHandle{}, err
+ }
+
+ return DatapathHandle{dpif: dpif, ifindex: dpi.ifindex}, nil
+}
+
+func IsDatapathNameAlreadyExistsError(err error) bool {
+ return err == NetlinkError(syscall.EEXIST)
+}
+
+func (dpif *Dpif) LookupDatapath(name string) (DatapathHandle, error) {
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[DATAPATH].id)
+ req.PutGenlMsghdr(OVS_DP_CMD_GET, OVS_DATAPATH_VERSION)
+ req.putOvsHeader(0)
+ req.PutStringAttr(OVS_DP_ATTR_NAME, name)
+
+ resp, err := dpif.sock.Request(req)
+ if err != nil {
+ return DatapathHandle{}, err
+ }
+
+ dpi, err := dpif.parseDatapathInfo(resp)
+ if err != nil {
+ return DatapathHandle{}, err
+ }
+
+ return DatapathHandle{dpif: dpif, ifindex: dpi.ifindex}, nil
+}
+
+type Datapath struct {
+ Handle DatapathHandle
+ Name string
+}
+
+func (dpif *Dpif) LookupDatapathByID(ifindex DatapathID) (Datapath, error) {
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[DATAPATH].id)
+ req.PutGenlMsghdr(OVS_DP_CMD_GET, OVS_DATAPATH_VERSION)
+ req.putOvsHeader(ifindex)
+
+ resp, err := dpif.sock.Request(req)
+ if err != nil {
+ return Datapath{}, err
+ }
+
+ dpi, err := dpif.parseDatapathInfo(resp)
+ if err != nil {
+ return Datapath{}, err
+ }
+
+ return Datapath{
+ Handle: DatapathHandle{dpif: dpif, ifindex: ifindex},
+ Name: dpi.name,
+ }, nil
+}
+
+func IsNoSuchDatapathError(err error) bool {
+ return err == NetlinkError(syscall.ENODEV)
+}
+
+func (dpif *Dpif) EnumerateDatapaths() (map[string]DatapathHandle, error) {
+ res := make(map[string]DatapathHandle)
+
+ req := NewNlMsgBuilder(DumpFlags, dpif.families[DATAPATH].id)
+ req.PutGenlMsghdr(OVS_DP_CMD_GET, OVS_DATAPATH_VERSION)
+ req.putOvsHeader(0)
+
+ consumer := func(resp *NlMsgParser) error {
+ dpi, err := dpif.parseDatapathInfo(resp)
+ if err != nil {
+ return err
+ }
+ res[dpi.name] = DatapathHandle{dpif: dpif, ifindex: dpi.ifindex}
+ return nil
+ }
+
+ err := dpif.sock.RequestMulti(req, consumer)
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+func (dp DatapathHandle) Delete() error {
+ req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[DATAPATH].id)
+ req.PutGenlMsghdr(OVS_DP_CMD_DEL, OVS_DATAPATH_VERSION)
+ req.putOvsHeader(dp.ifindex)
+
+ _, err := dp.dpif.sock.Request(req)
+ if err != nil {
+ return err
+ }
+
+ dp.dpif = nil
+ dp.ifindex = 0
+ return nil
+}
+
+func (dp DatapathHandle) checkNlMsgHeaders(msg *NlMsgParser, family int, cmd int) error {
+ _, ovshdr, err := dp.dpif.checkNlMsgHeaders(msg, family, cmd)
+ if err != nil {
+ return err
+ }
+
+ if ovshdr.datapathID() != dp.ifindex {
+ return fmt.Errorf("wrong datapath ifindex received (got %d, expected %d)", ovshdr.datapathID(), dp.ifindex)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/dpif.go b/vendor/github.com/weaveworks/go-odp/odp/dpif.go
new file mode 100644
index 000000000..520523142
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/dpif.go
@@ -0,0 +1,181 @@
+package odp
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ DATAPATH = iota
+ VPORT = iota
+ FLOW = iota
+ PACKET = iota
+ FAMILY_COUNT = iota
+)
+
+var familyNames = [FAMILY_COUNT]string{
+ "ovs_datapath",
+ "ovs_vport",
+ "ovs_flow",
+ "ovs_packet",
+}
+
+type Dpif struct {
+ sock *NetlinkSocket
+ families [FAMILY_COUNT]GenlFamily
+}
+
+type familyUnavailableError struct {
+ family string
+}
+
+func (fue familyUnavailableError) Error() string {
+ return fmt.Sprintf("Generic netlink family '%s' unavailable; the Open vSwitch kernel module is probably not loaded, try 'modprobe openvswitch'", fue.family)
+}
+
+func IsKernelLacksODPError(err error) bool {
+ _, ok := err.(familyUnavailableError)
+ return ok
+}
+
+func lookupFamily(sock *NetlinkSocket, name string) (GenlFamily, error) {
+ family, err := sock.LookupGenlFamily(name)
+ if err == nil {
+ return family, nil
+ }
+
+ if err == NetlinkError(syscall.ENOENT) {
+ loadOpenvswitchModule()
+
+ // The module might be loaded now, so try again
+ family, err = sock.LookupGenlFamily(name)
+ if err == nil {
+ return family, nil
+ }
+
+ if err == NetlinkError(syscall.ENOENT) {
+ err = familyUnavailableError{name}
+ }
+ }
+
+ return GenlFamily{}, err
+}
+
+var triedLoadOpenvswitchModule bool
+
+// This tries to provoke the kernel into loading the openvswitch
+// module. Yes, netdev ioctls can be used to load arbitrary modules,
+// if you have CAP_SYS_MODULE.
+func loadOpenvswitchModule() {
+ if triedLoadOpenvswitchModule {
+ return
+ }
+
+ // netdev ioctls don't seem to work on netlink sockets, so we
+ // need a new socket for this purpose.
+ s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
+ if err != nil {
+ triedLoadOpenvswitchModule = true
+ return
+ }
+
+ defer syscall.Close(s)
+
+ var req ifreqIfindex
+ copy(req.name[:], []byte("openvswitch"))
+ syscall.Syscall(syscall.SYS_IOCTL, uintptr(s),
+ syscall.SIOCGIFINDEX, uintptr(unsafe.Pointer(&req)))
+ triedLoadOpenvswitchModule = true
+}
+
+func NewDpif() (*Dpif, error) {
+ sock, err := OpenNetlinkSocket(syscall.NETLINK_GENERIC)
+ if err != nil {
+ return nil, err
+ }
+
+ dpif := &Dpif{sock: sock}
+
+ for i := 0; i < FAMILY_COUNT; i++ {
+ dpif.families[i], err = lookupFamily(sock, familyNames[i])
+ if err != nil {
+ sock.Close()
+ return nil, err
+ }
+ }
+
+ return dpif, nil
+}
+
+// Open a dpif with a new socket, but reuing the family info
+func (dpif *Dpif) Reopen() (*Dpif, error) {
+ sock, err := OpenNetlinkSocket(syscall.NETLINK_GENERIC)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Dpif{sock: sock, families: dpif.families}, nil
+}
+
+func (dpif *Dpif) getMCGroup(family int, name string) (uint32, error) {
+ mcGroup, ok := dpif.families[family].mcGroups[name]
+ if !ok {
+ return 0, fmt.Errorf("No genl MC group %s in family %s", name, familyNames[family])
+ }
+
+ return mcGroup, nil
+}
+
+func (dpif *Dpif) Close() error {
+ return dpif.sock.Close()
+}
+
+func (nlmsg *NlMsgBuilder) putOvsHeader(ifindex DatapathID) {
+ pos := nlmsg.AlignGrow(syscall.NLMSG_ALIGNTO, SizeofOvsHeader)
+ h := ovsHeaderAt(nlmsg.buf, pos)
+ h.DpIfIndex = int32(ifindex)
+}
+
+func (nlmsg *NlMsgParser) takeOvsHeader() (*OvsHeader, error) {
+ pos, err := nlmsg.AlignAdvance(syscall.NLMSG_ALIGNTO, SizeofOvsHeader)
+ if err != nil {
+ return nil, err
+ }
+
+ return ovsHeaderAt(nlmsg.data, pos), nil
+}
+
+func (ovshdr OvsHeader) datapathID() DatapathID {
+ return DatapathID(ovshdr.DpIfIndex)
+}
+
+func (dpif *Dpif) checkNlMsgHeaders(msg *NlMsgParser, family int, cmd int) (*GenlMsghdr, *OvsHeader, error) {
+ if _, err := msg.ExpectNlMsghdr(dpif.families[family].id); err != nil {
+ return nil, nil, err
+ }
+
+ genlhdr, err := msg.CheckGenlMsghdr(cmd)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ ovshdr, err := msg.takeOvsHeader()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return genlhdr, ovshdr, nil
+}
+
+type Cancelable interface {
+ Cancel() error
+}
+
+type cancelableDpif struct {
+ *Dpif
+}
+
+func (dpif cancelableDpif) Cancel() error {
+ return dpif.Close()
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/dpif_test.go b/vendor/github.com/weaveworks/go-odp/odp/dpif_test.go
new file mode 100644
index 000000000..0045d9a48
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/dpif_test.go
@@ -0,0 +1,437 @@
+package odp
+
+import (
+ "fmt"
+ "math/rand"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func init() {
+ rand.Seed(time.Now().UTC().UnixNano())
+}
+
+func checkedCloseDpif(dpif *Dpif, t *testing.T) {
+ err := dpif.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestCreateDatapath(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ name := fmt.Sprintf("test%d", rand.Intn(100000))
+
+ dp, err := dpif.CreateDatapath(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = dp.Delete()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLookupDatapath(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ name := fmt.Sprintf("test%d", rand.Intn(100000))
+ dp, err := dpif.LookupDatapath(name)
+ if !IsNoSuchDatapathError(err) {
+ t.Fatal(err)
+ }
+
+ _, err = dpif.CreateDatapath(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ checkedCloseDpif(dpif, t)
+ dpif, err = NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dp, err = dpif.LookupDatapath(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = dp.Delete()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestEnumerateDatapaths(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ var names []string
+ var dps []DatapathHandle
+
+ cleanup := func() {
+ for _, dp := range dps {
+ dp.Delete()
+ }
+ }
+
+ defer cleanup()
+
+ for i := 0; i < 10; i++ {
+ name := fmt.Sprintf("test%d", rand.Intn(100000))
+ dp, err := dpif.CreateDatapath(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ names = append(names, name)
+ dps = append(dps, dp)
+ }
+
+ name2dp, err := dpif.EnumerateDatapaths()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, name := range names {
+ _, ok := name2dp[name]
+ if !ok {
+ t.Fatal()
+ }
+ }
+
+ cleanup()
+
+ name2dp, err = dpif.EnumerateDatapaths()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, name := range names {
+ _, ok := name2dp[name]
+ if ok {
+ t.Fatal()
+ }
+ }
+}
+
+func checkedDeleteDatapath(dp DatapathHandle, t *testing.T) {
+ err := dp.Delete()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestCreateVport(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dp, err := dpif.CreateDatapath(fmt.Sprintf("test%d", rand.Intn(100000)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedDeleteDatapath(dp, t)
+
+ name := fmt.Sprintf("test%d", rand.Intn(100000))
+ vport, err := dp.CreateVport(NewInternalVportSpec(name))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = dp.DeleteVport(vport)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLookupVport(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dpname := fmt.Sprintf("test%d", rand.Intn(100000))
+ dp, err := dpif.CreateDatapath(dpname)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ name := fmt.Sprintf("test%d", rand.Intn(100000))
+ vport, err := dp.LookupVportByName(name)
+ if !IsNoSuchVportError(err) {
+ t.Fatal(err)
+ }
+
+ _, err = dp.CreateVport(NewInternalVportSpec(name))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ checkedCloseDpif(dpif, t)
+ dpif, err = NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dp, err = dpif.LookupDatapath(dpname)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer dp.Delete()
+
+ vport, err = dp.LookupVportByName(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = dp.DeleteVport(vport.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestEnumerateVports(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dp, err := dpif.CreateDatapath(fmt.Sprintf("test%d", rand.Intn(100000)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedDeleteDatapath(dp, t)
+
+ var names []string
+ var vports []VportID
+
+ for i := 0; i < 10; i++ {
+ name := fmt.Sprintf("test%d", rand.Intn(100000))
+ vport, err := dp.CreateVport(NewInternalVportSpec(name))
+ if err != nil {
+ t.Fatal(err)
+ }
+ names = append(names, name)
+ vports = append(vports, vport)
+ }
+
+ gotvports, err := dp.EnumerateVports()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotnames := make(map[string]bool)
+ for _, vport := range gotvports {
+ gotnames[vport.Spec.Name()] = true
+ }
+
+ for _, name := range names {
+ _, ok := gotnames[name]
+ if !ok {
+ t.Fatal()
+ }
+ }
+
+ for _, vport := range vports {
+ dp.DeleteVport(vport)
+ }
+
+ gotvports, err = dp.EnumerateVports()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotnames = make(map[string]bool)
+ for _, vport := range gotvports {
+ gotnames[vport.Spec.Name()] = true
+ }
+
+ for _, name := range names {
+ _, ok := gotnames[name]
+ if ok {
+ t.Fatal()
+ }
+ }
+}
+
+var exactOvsKeyEthernetMask OvsKeyEthernet = OvsKeyEthernet{
+ EthSrc: [...]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ EthDst: [...]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+}
+
+func TestCreateFlow(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dp, err := dpif.CreateDatapath(fmt.Sprintf("test%d", rand.Intn(100000)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedDeleteDatapath(dp, t)
+
+ vpname := fmt.Sprintf("test%d", rand.Intn(100000))
+ vport, err := dp.CreateVport(NewInternalVportSpec(vpname))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ f := NewFlowSpec()
+ fk := NewEthernetFlowKey()
+ fk.SetEthSrc([...]byte{1, 2, 3, 4, 5, 6})
+ fk.SetEthDst([...]byte{1, 2, 3, 4, 5, 6})
+ f.AddKey(fk)
+ f.AddAction(NewOutputAction(vport))
+
+ err = dp.CreateFlow(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = dp.DeleteFlow(f.FlowKeys)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = dp.DeleteFlow(f.FlowKeys)
+ if !IsNoSuchFlowError(err) {
+ t.Fatal()
+ }
+}
+
+func TestEnumerateFlows(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedCloseDpif(dpif, t)
+
+ dp, err := dpif.CreateDatapath(fmt.Sprintf("test%d", rand.Intn(100000)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer checkedDeleteDatapath(dp, t)
+
+ vpname := fmt.Sprintf("test%d", rand.Intn(100000))
+ vport, err := dp.CreateVport(NewInternalVportSpec(vpname))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ const n = 10
+ var flows [n]FlowSpec
+
+ for i := range flows {
+ flow := NewFlowSpec()
+ fk := NewEthernetFlowKey()
+ fk.SetEthSrc([...]byte{1, 2, 3, 4, 5, byte(i)})
+ fk.SetEthDst([...]byte{6, 5, 4, 3, 2, 1})
+ flow.AddKey(fk)
+ flow.AddAction(NewOutputAction(vport))
+ err = dp.CreateFlow(flow)
+ if err != nil {
+ t.Fatal(err)
+ }
+ flows[i] = flow
+ }
+
+ eflows, err := dp.EnumerateFlows()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(eflows) != n {
+ t.Fatal()
+ }
+
+ for _, eflow := range eflows {
+ found := false
+
+ for _, flow := range flows {
+ if eflow.Equals(flow) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Fatal(eflow)
+ }
+ }
+
+ for _, eflow := range eflows {
+ err = dp.DeleteFlow(eflow.FlowKeys)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ eflows, err = dp.EnumerateFlows()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(eflows) != 0 {
+ t.Fatal()
+ }
+}
+
+func TestConsumeVportEvents(t *testing.T) {
+ dpif, err := NewDpif()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ time.Sleep(100 * time.Millisecond)
+
+ ch := make(chan error)
+ cancel, err := dpif.ConsumeVportEvents(vportTestConsumer{ch})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err := cancel.Cancel(); err != nil {
+ t.Fatal(err)
+ }
+
+ if <-ch != syscall.EBADF {
+ t.Fatal()
+ }
+}
+
+type vportTestConsumer struct {
+ ch chan error
+}
+
+func (vportTestConsumer) VportCreated(ifindex int32, vport Vport) error {
+ return nil
+}
+
+func (vportTestConsumer) VportDeleted(ifindex int32, vport Vport) error {
+ return nil
+}
+
+func (consumer vportTestConsumer) Error(err error, stopped bool) {
+ consumer.ch <- err
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/flow.go b/vendor/github.com/weaveworks/go-odp/odp/flow.go
new file mode 100644
index 000000000..38531b83d
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/flow.go
@@ -0,0 +1,1349 @@
+package odp
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "net"
+ "syscall"
+)
+
+func AllBytes(data []byte, x byte) bool {
+ for _, y := range data {
+ if x != y {
+ return false
+ }
+ }
+
+ return true
+}
+
+type FlowKey interface {
+ typeId() uint16
+ putKeyNlAttr(*NlMsgBuilder)
+ putMaskNlAttr(*NlMsgBuilder) error
+ Ignored() bool
+ Equals(FlowKey) bool
+}
+
+type FlowKeys map[uint16]FlowKey
+
+func (a FlowKeys) Equals(b FlowKeys) bool {
+ for id, ak := range a {
+ bk, ok := b[id]
+ if ok {
+ if !ak.Equals(bk) {
+ return false
+ }
+ } else {
+ if !ak.Ignored() {
+ return false
+ }
+ }
+ }
+
+ for id, bk := range b {
+ _, ok := a[id]
+ if !ok && !bk.Ignored() {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (fks FlowKeys) toNlAttrs(msg *NlMsgBuilder) error {
+ // The ethernet flow key is mandatory, even if it is
+ // completely wildcarded.
+ var defaultEthernetFlowKey FlowKey
+ if fks[OVS_KEY_ATTR_ETHERNET] == nil {
+ defaultEthernetFlowKey = NewEthernetFlowKey()
+ }
+
+ msg.PutNestedAttrs(OVS_FLOW_ATTR_KEY, func() {
+ for _, k := range fks {
+ if !k.Ignored() {
+ k.putKeyNlAttr(msg)
+ }
+ }
+
+ if defaultEthernetFlowKey != nil {
+ defaultEthernetFlowKey.putKeyNlAttr(msg)
+ }
+ })
+
+ var err error
+ msg.PutNestedAttrs(OVS_FLOW_ATTR_MASK, func() {
+ for _, k := range fks {
+ if !k.Ignored() {
+ if e := k.putMaskNlAttr(msg); e != nil {
+ err = e
+ }
+ }
+ }
+
+ if defaultEthernetFlowKey != nil {
+ defaultEthernetFlowKey.putMaskNlAttr(msg)
+ }
+ })
+
+ return err
+}
+
+// A FlowKeyParser describes how to parse a flow key of a particular
+// type from a netlnk message
+type FlowKeyParser struct {
+ // Flow key parsing function
+ //
+ // key may be nil if the relevant attribute wasn't provided.
+ // This generally means that the mask will indicate that the
+ // flow key is Ignored.
+ parse func(typ uint16, key []byte, mask []byte, exact bool) (FlowKey, error)
+
+ // Special mask values indicating that the flow key is an
+ // exact match or Ignored. The parse function also receives
+ // an "exact" flag, to handle cases where the representation
+ // of the mask of awkward.
+ exactMask []byte
+ ignoreMask []byte
+}
+
+// Maps an NL attribute type to the corresponding FlowKeyParser
+type FlowKeyParsers map[uint16]FlowKeyParser
+
+func ParseFlowKeys(keys Attrs, masks Attrs) (res FlowKeys, err error) {
+ res = make(FlowKeys)
+
+ for typ, key := range keys {
+ parser, ok := flowKeyParsers[typ]
+ if !ok {
+ parser = FlowKeyParser{parse: parseUnknownFlowKey}
+ }
+
+ var mask []byte
+ exact := false
+ if masks == nil {
+ // "OVS_FLOW_ATTR_MASK: ... If not present,
+ // all flow key bits are exact match bits."
+ mask = parser.exactMask
+ exact = true
+ } else {
+ // "Omitting attribute is treated as
+ // wildcarding all corresponding fields"
+ mask, ok = masks[typ]
+ if !ok {
+ mask = parser.ignoreMask
+ }
+ }
+
+ res[typ], err = parser.parse(typ, key, mask, exact)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if masks != nil {
+ for typ, mask := range masks {
+ _, ok := keys[typ]
+ if ok {
+ continue
+ }
+
+ // flow key mask without a corresponding flow
+ // key value
+ parser, ok := flowKeyParsers[typ]
+ if !ok {
+ parser = FlowKeyParser{parse: parseUnknownFlowKey}
+ }
+
+ res[typ], err = parser.parse(typ, nil, mask, false)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return res, nil
+}
+
+// A flow key of a type we don't know about
+type UnknownFlowKey struct {
+ typ uint16
+ key []byte
+ mask []byte // nil means ignored
+ exact bool
+}
+
+func parseUnknownFlowKey(typ uint16, key []byte, mask []byte, exact bool) (FlowKey, error) {
+ return UnknownFlowKey{typ: typ, key: key, mask: mask, exact: exact}, nil
+}
+
+func (key UnknownFlowKey) String() string {
+ var mask string
+ switch {
+ case key.exact:
+ mask = "exact"
+ case key.mask == nil:
+ mask = "ignored"
+ default:
+ mask = hex.EncodeToString(key.mask)
+ }
+
+ return fmt.Sprintf("UnknownFlowKey{type: %d, key: %s, mask: %s}",
+ key.typ, hex.EncodeToString(key.key), mask)
+}
+
+func (key UnknownFlowKey) typeId() uint16 {
+ return key.typ
+}
+
+func (key UnknownFlowKey) putKeyNlAttr(msg *NlMsgBuilder) {
+ msg.PutSliceAttr(key.typ, key.key)
+}
+
+func (key UnknownFlowKey) putMaskNlAttr(msg *NlMsgBuilder) error {
+ if key.exact {
+ return fmt.Errorf("cannot serialize exact mask for unknown flow key of type %d", key.typ)
+ }
+
+ if key.mask != nil {
+ msg.PutSliceAttr(key.typ, key.mask)
+ }
+
+ return nil
+}
+
+func (key UnknownFlowKey) Ignored() bool {
+ return key.mask == nil && !key.exact
+}
+
+func (a UnknownFlowKey) Equals(gb FlowKey) bool {
+ b, ok := gb.(UnknownFlowKey)
+ if !ok {
+ return false
+ }
+
+ if a.typ != b.typ || !bytes.Equal(a.key, b.key) {
+ return false
+ }
+
+ switch {
+ case a.exact:
+ return b.exact
+ case a.mask == nil:
+ return b.mask == nil
+ default:
+ return bytes.Equal(a.mask, b.mask)
+ }
+}
+
+// Most flow keys can be handled as opaque bytes.
+type BlobFlowKey struct {
+ typ uint16
+
+ // This holds the key and the mask concatenated, so it is
+ // twice their length
+ keyMask []byte
+}
+
+func NewBlobFlowKey(typ uint16, size int) BlobFlowKey {
+ km := MakeAlignedByteSlice(size * 2)
+ mask := km[size:]
+ for i := range mask {
+ mask[i] = 0xff
+ }
+ return BlobFlowKey{typ: typ, keyMask: km}
+}
+
+func (key BlobFlowKey) String() string {
+ return fmt.Sprintf("BlobFlowKey{type: %d, key: %s, mask: %s}", key.typ,
+ hex.EncodeToString(key.key()), hex.EncodeToString(key.mask()))
+}
+
+func (key BlobFlowKey) typeId() uint16 {
+ return key.typ
+}
+
+func (key BlobFlowKey) key() []byte {
+ return key.keyMask[:len(key.keyMask)/2]
+}
+
+func (key BlobFlowKey) mask() []byte {
+ return key.keyMask[len(key.keyMask)/2:]
+}
+
+func (key BlobFlowKey) putKeyNlAttr(msg *NlMsgBuilder) {
+ msg.PutSliceAttr(key.typ, key.key())
+}
+
+func (key BlobFlowKey) putMaskNlAttr(msg *NlMsgBuilder) error {
+ msg.PutSliceAttr(key.typ, key.mask())
+ return nil
+}
+
+func (key BlobFlowKey) Ignored() bool {
+ return AllBytes(key.mask(), 0)
+}
+
+// Go's anonymous struct fields are not quite a replacement for
+// inheritance. We want to have an Equals method for BlobFlowKeys,
+// that works even when BlobFlowKeys are embedded as anonymous struct
+// fields. But we can't use a straightforward type assertion to tell
+// if another FlowKey is also a BlobFlowKey, because in the embedded
+// case, it will say that the FlowKey is not an BlobFlowKey (the "has
+// an anonymoys field of X" is not an "is a X" relation). To work
+// around this, we use an interface, implemented by BlobFlowKey, that
+// automatically gets promoted to all structs that embed BlobFlowKey.
+
+type BlobFlowKeyish interface {
+ toBlobFlowKey() BlobFlowKey
+}
+
+func (key BlobFlowKey) toBlobFlowKey() BlobFlowKey { return key }
+
+func (a BlobFlowKey) Equals(gb FlowKey) bool {
+ bx, ok := gb.(BlobFlowKeyish)
+ if !ok {
+ return false
+ }
+ b := bx.toBlobFlowKey()
+
+ if a.typ != b.typ {
+ return false
+ }
+
+ size := len(a.keyMask)
+ if len(b.keyMask) != size {
+ return false
+ }
+ size /= 2
+
+ amask := a.keyMask[size:]
+ bmask := b.keyMask[size:]
+ for i := range amask {
+ if amask[i] != bmask[i] || ((a.keyMask[i]^b.keyMask[i])&amask[i]) != 0 {
+ return false
+ }
+ }
+
+ return true
+}
+
+func parseBlobFlowKey(typ uint16, key []byte, mask []byte, size int) (BlobFlowKey, error) {
+ res := BlobFlowKey{typ: typ}
+
+ if len(mask) != size {
+ return res, fmt.Errorf("flow key mask type %d has wrong length (expected %d bytes, got %d)", typ, size, len(mask))
+ }
+
+ res.keyMask = MakeAlignedByteSlice(size * 2)
+ copy(res.keyMask[size:], mask)
+
+ if key != nil {
+ if len(key) != size {
+ return res, fmt.Errorf("flow key type %d has wrong length (expected %d bytes, got %d)", typ, size, len(key))
+ }
+
+ copy(res.keyMask, key)
+ } else {
+ // The kernel produces masks without a corresponding
+ // key, but in such cases the mask should indicate
+ // that the key value is ignored.
+ if !AllBytes(mask, 0) {
+ return res, fmt.Errorf("flow key type %d has non-zero mask without a value (mask %v)", typ, mask)
+ }
+ }
+
+ return res, nil
+}
+
+func blobFlowKeyParser(size int, wrap func(BlobFlowKey) FlowKey) FlowKeyParser {
+ exact := make([]byte, size)
+ for i := range exact {
+ exact[i] = 0xff
+ }
+
+ return FlowKeyParser{
+ parse: func(typ uint16, key []byte, mask []byte, exact bool) (FlowKey, error) {
+ bfk, err := parseBlobFlowKey(typ, key, mask, size)
+ if err != nil {
+ return nil, err
+ }
+ if wrap == nil {
+ return bfk, nil
+ } else {
+ return wrap(bfk), nil
+ }
+ },
+ ignoreMask: make([]byte, size),
+ exactMask: exact,
+ }
+}
+
+// OVS_KEY_ATTR_IN_PORT: Incoming port number
+//
+// This flow key is problematic. First, the kernel always does an
+// exact match for IN_PORT, i.e. it takes the mask to be 0xffffffff if
+// the key is set at all. Second, when reporting the mask, the kernel
+// always sets the upper 16 bits, probably because port numbers are 16
+// bits in the kernel, but 32 bits in the ABI to userspace. It does
+// this even if the IN_PORT flow key was not set. As a result, we
+// take any mask other than 0xffffffff to mean ignored.
+
+type InPortFlowKey struct {
+ BlobFlowKey
+}
+
+func parseInPortFlowKey(typ uint16, key []byte, mask []byte, exact bool) (FlowKey, error) {
+ if !AllBytes(mask, 0xff) {
+ for i := range mask {
+ mask[i] = 0
+ }
+ }
+ fk, err := parseBlobFlowKey(typ, key, mask, 4)
+ if err != nil {
+ return nil, err
+ }
+ return InPortFlowKey{fk}, nil
+}
+
+func NewInPortFlowKey(vport VportID) FlowKey {
+ fk := InPortFlowKey{NewBlobFlowKey(OVS_KEY_ATTR_IN_PORT, 4)}
+ *uint32At(fk.key(), 0) = uint32(vport)
+ return fk
+}
+
+func (key InPortFlowKey) String() string {
+ return fmt.Sprintf("InPortFlowKey{vport: %d}", key.VportID())
+}
+
+func (k InPortFlowKey) VportID() VportID {
+ return VportID(*uint32At(k.key(), 0))
+}
+
+// OVS_KEY_ATTR_ETHERNET: Ethernet header flow key
+
+type EthernetFlowKey struct {
+ BlobFlowKey
+}
+
+func (key EthernetFlowKey) Ignored() bool {
+ // An ethernet flow key is mandatory, so don't omit it just
+ // because the mask is all zeros
+ return false
+}
+
+func NewEthernetFlowKey() EthernetFlowKey {
+ return EthernetFlowKey{NewBlobFlowKey(OVS_KEY_ATTR_ETHERNET,
+ SizeofOvsKeyEthernet)}
+}
+
+func (fk *EthernetFlowKey) key() *OvsKeyEthernet {
+ return ovsKeyEthernetAt(fk.BlobFlowKey.key(), 0)
+}
+
+func (fk *EthernetFlowKey) mask() *OvsKeyEthernet {
+ return ovsKeyEthernetAt(fk.BlobFlowKey.mask(), 0)
+}
+
+func (fk EthernetFlowKey) Key() OvsKeyEthernet {
+ return *fk.key()
+}
+
+func (fk EthernetFlowKey) Mask() OvsKeyEthernet {
+ return *fk.mask()
+}
+
+func (fk *EthernetFlowKey) SetMaskedEthSrc(addr [ETH_ALEN]byte,
+ mask [ETH_ALEN]byte) {
+ fk.key().EthSrc = addr
+ fk.mask().EthSrc = mask
+}
+
+func (fk *EthernetFlowKey) SetEthSrc(addr [ETH_ALEN]byte) {
+ fk.SetMaskedEthSrc(addr, [...]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
+}
+
+func (fk *EthernetFlowKey) SetMaskedEthDst(addr [ETH_ALEN]byte,
+ mask [ETH_ALEN]byte) {
+ fk.key().EthDst = addr
+ fk.mask().EthDst = mask
+}
+
+func (fk *EthernetFlowKey) SetEthDst(addr [ETH_ALEN]byte) {
+ fk.SetMaskedEthDst(addr, [...]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
+}
+
+func (fk EthernetFlowKey) String() string {
+ var buf bytes.Buffer
+ var sep string
+ fmt.Fprint(&buf, "EthernetFlowKey{")
+
+ if fk.typ != OVS_KEY_ATTR_ETHERNET {
+ fmt.Fprintf(&buf, "type: %d", fk.typ)
+ sep = ", "
+ }
+
+ k := fk.Key()
+ m := fk.Mask()
+ ha := func(s []byte) string { return net.HardwareAddr(s).String() }
+ printMaskedBytes(&buf, &sep, "src", k.EthSrc[:], m.EthSrc[:], ha)
+ printMaskedBytes(&buf, &sep, "dst", k.EthDst[:], m.EthDst[:], ha)
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+func printMaskedBytes(buf *bytes.Buffer, sep *string, n string, k, m []byte,
+ s func([]byte) string) {
+ if !AllBytes(m, 0) {
+ fmt.Fprintf(buf, "%s%s: %s", *sep, n, s(k))
+ if !AllBytes(m, 0xff) {
+ fmt.Fprintf(buf, "&%s", s(m))
+ }
+
+ *sep = ", "
+ }
+}
+
+var ethernetFlowKeyParser = blobFlowKeyParser(SizeofOvsKeyEthernet,
+ func(fk BlobFlowKey) FlowKey { return EthernetFlowKey{fk} })
+
+// OVS_KEY_ATTR_TUNNEL: Tunnel flow key. This is more elaborate than
+// other flow keys because it consists of a set of attributes.
+
+type TunnelAttrs struct {
+ TunnelId [8]byte
+ Ipv4Src [4]byte
+ Ipv4Dst [4]byte
+ Tos uint8
+ Ttl uint8
+ Df bool
+ Csum bool
+ TpSrc uint16
+ TpDst uint16
+}
+
+type TunnelAttrsPresence struct {
+ TunnelId bool
+ Ipv4Src bool
+ Ipv4Dst bool
+ Tos bool
+ Ttl bool
+ Df bool
+ Csum bool
+ TpSrc bool
+ TpDst bool
+}
+
+// Extract presence information from a TunnelAttrs mask
+func (ta TunnelAttrs) present() TunnelAttrsPresence {
+ // The kernel requires Ipv4Dst and Ttl to be present, so we
+ // always mark those as present, even if we end up wildcarding
+ // them.
+ return TunnelAttrsPresence{
+ TunnelId: !AllBytes(ta.TunnelId[:], 0),
+ Ipv4Src: !AllBytes(ta.Ipv4Src[:], 0),
+ Ipv4Dst: true,
+ Tos: ta.Tos != 0,
+ Ttl: true,
+ Df: ta.Df,
+ Csum: ta.Csum,
+ TpSrc: ta.TpSrc != 0,
+ TpDst: ta.TpDst != 0,
+ }
+}
+
+// Convert a TunnelAttrsPresence to a mask
+func (tap TunnelAttrsPresence) mask() (res TunnelAttrs) {
+ if tap.TunnelId {
+ res.TunnelId = [8]byte{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ }
+ }
+
+ if tap.Ipv4Src {
+ res.Ipv4Src = [4]byte{0xff, 0xff, 0xff, 0xff}
+ }
+
+ if tap.Ipv4Dst {
+ res.Ipv4Dst = [4]byte{0xff, 0xff, 0xff, 0xff}
+ }
+
+ if tap.Tos {
+ res.Tos = 0xff
+ }
+
+ if tap.Ttl {
+ res.Ttl = 0xff
+ }
+
+ res.Df = tap.Df
+ res.Csum = tap.Csum
+
+ if tap.TpSrc {
+ res.TpSrc = 0xffff
+ }
+
+ if tap.TpDst {
+ res.TpDst = 0xffff
+ }
+
+ return
+}
+
+func (ta TunnelAttrs) toNlAttrs(msg *NlMsgBuilder, present TunnelAttrsPresence) {
+ if present.TunnelId {
+ msg.PutSliceAttr(OVS_TUNNEL_KEY_ATTR_ID, ta.TunnelId[:])
+ }
+
+ if present.Ipv4Src {
+ msg.PutSliceAttr(OVS_TUNNEL_KEY_ATTR_IPV4_SRC, ta.Ipv4Src[:])
+ }
+
+ if present.Ipv4Dst {
+ msg.PutSliceAttr(OVS_TUNNEL_KEY_ATTR_IPV4_DST, ta.Ipv4Dst[:])
+ }
+
+ if present.Tos {
+ msg.PutUint8Attr(OVS_TUNNEL_KEY_ATTR_TOS, ta.Tos)
+ }
+
+ if present.Ttl {
+ msg.PutUint8Attr(OVS_TUNNEL_KEY_ATTR_TTL, ta.Ttl)
+ }
+
+ if present.Df && ta.Df {
+ msg.PutEmptyAttr(OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)
+ }
+
+ if present.Csum && ta.Csum {
+ msg.PutEmptyAttr(OVS_TUNNEL_KEY_ATTR_CSUM)
+ }
+
+ if present.TpSrc {
+ msg.PutUint16Attr(OVS_TUNNEL_KEY_ATTR_TP_SRC,
+ uint16ToBE(ta.TpSrc))
+ }
+
+ if present.TpDst {
+ msg.PutUint16Attr(OVS_TUNNEL_KEY_ATTR_TP_DST,
+ uint16ToBE(ta.TpDst))
+ }
+}
+
+func parseTunnelAttrs(data []byte) (ta TunnelAttrs, present TunnelAttrsPresence, err error) {
+ attrs, err := ParseNestedAttrs(data)
+ if err != nil {
+ return
+ }
+
+ present.TunnelId, err = attrs.GetOptionalBytes(OVS_TUNNEL_KEY_ATTR_ID, ta.TunnelId[:])
+ if err != nil {
+ return
+ }
+
+ present.Ipv4Src, err = attrs.GetOptionalBytes(OVS_TUNNEL_KEY_ATTR_IPV4_SRC, ta.Ipv4Src[:])
+ if err != nil {
+ return
+ }
+
+ present.Ipv4Dst, err = attrs.GetOptionalBytes(OVS_TUNNEL_KEY_ATTR_IPV4_DST, ta.Ipv4Dst[:])
+
+ ta.Tos, present.Tos, err = attrs.GetOptionalUint8(OVS_TUNNEL_KEY_ATTR_TOS)
+ if err != nil {
+ return
+ }
+
+ ta.Ttl, present.Ttl, err = attrs.GetOptionalUint8(OVS_TUNNEL_KEY_ATTR_TTL)
+ if err != nil {
+ return
+ }
+
+ ta.Df, err = attrs.GetEmpty(OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)
+ present.Df = ta.Df
+ if err != nil {
+ return
+ }
+
+ ta.Csum, err = attrs.GetEmpty(OVS_TUNNEL_KEY_ATTR_CSUM)
+ present.Csum = ta.Csum
+ if err != nil {
+ return
+ }
+
+ ta.TpSrc, present.TpSrc, err = attrs.GetOptionalUint16(OVS_TUNNEL_KEY_ATTR_TP_SRC)
+ if err != nil {
+ return
+ }
+ ta.TpSrc = uint16FromBE(ta.TpSrc)
+
+ ta.TpDst, present.TpDst, err = attrs.GetOptionalUint16(OVS_TUNNEL_KEY_ATTR_TP_DST)
+ if err != nil {
+ return
+ }
+ ta.TpDst = uint16FromBE(ta.TpDst)
+
+ return
+}
+
+type TunnelFlowKey struct {
+ key TunnelAttrs
+ mask TunnelAttrs
+}
+
+func (fk TunnelFlowKey) String() string {
+ var buf bytes.Buffer
+ var sep string
+ fmt.Fprint(&buf, "TunnelFlowKey{")
+
+ printMaskedBytes(&buf, &sep, "id", fk.key.TunnelId[:],
+ fk.mask.TunnelId[:], hex.EncodeToString)
+ printMaskedBytes(&buf, &sep, "ipv4src", fk.key.Ipv4Src[:],
+ fk.mask.Ipv4Src[:], ipv4ToString)
+ printMaskedBytes(&buf, &sep, "ipv4dst", fk.key.Ipv4Dst[:],
+ fk.mask.Ipv4Dst[:], ipv4ToString)
+
+ printByte := func(n string, k, m byte) {
+ if m != 0 {
+ fmt.Fprintf(&buf, "%s%s: %d", sep, n, k)
+ if m != 0xff {
+ fmt.Fprintf(&buf, "&%x", m)
+ }
+ sep = ", "
+ }
+ }
+
+ printByte("tos", fk.key.Tos, fk.mask.Tos)
+ printByte("ttl", fk.key.Ttl, fk.mask.Ttl)
+
+ if fk.mask.Df {
+ fmt.Fprintf(&buf, "%sdf: %t", sep, fk.key.Df)
+ sep = ", "
+ }
+
+ if fk.mask.Csum {
+ fmt.Fprintf(&buf, "%ssum: %t", sep, fk.key.Csum)
+ sep = ", "
+ }
+
+ printUint16 := func(n string, k, m uint16) {
+ if m != 0 {
+ fmt.Fprintf(&buf, "%s%s: %d", sep, n, k)
+ if m != 0xffff {
+ fmt.Fprintf(&buf, "&%x", m)
+ }
+ sep = ", "
+ }
+ }
+
+ printUint16("tpsrc", fk.key.TpSrc, fk.mask.TpSrc)
+ printUint16("tpdst", fk.key.TpDst, fk.mask.TpDst)
+
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+func ipv4ToString(ip []byte) string {
+ return net.IP(ip).To4().String()
+}
+
+func (fk TunnelFlowKey) Key() TunnelAttrs {
+ return fk.key
+}
+
+func (fk TunnelFlowKey) Mask() TunnelAttrs {
+ return fk.mask
+}
+
+func (TunnelFlowKey) typeId() uint16 {
+ return OVS_KEY_ATTR_TUNNEL
+}
+
+func (fk *TunnelFlowKey) SetTunnelId(id [8]byte) {
+ fk.key.TunnelId = id
+ fk.mask.TunnelId = [...]byte{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ }
+}
+
+func (fk *TunnelFlowKey) SetIpv4Src(addr [4]byte) {
+ fk.key.Ipv4Src = addr
+ fk.mask.Ipv4Src = [...]byte{0xff, 0xff, 0xff, 0xff}
+}
+
+func (fk *TunnelFlowKey) SetIpv4Dst(addr [4]byte) {
+ fk.key.Ipv4Dst = addr
+ fk.mask.Ipv4Dst = [...]byte{0xff, 0xff, 0xff, 0xff}
+}
+
+func (fk *TunnelFlowKey) SetTos(tos uint8) {
+ fk.key.Tos = tos
+ fk.mask.Tos = 0xff
+}
+
+func (fk *TunnelFlowKey) SetTtl(ttl uint8) {
+ fk.key.Ttl = ttl
+ fk.mask.Ttl = 0xff
+}
+
+func (fk *TunnelFlowKey) SetDf(df bool) {
+ fk.key.Df = df
+ fk.mask.Df = true
+}
+
+func (fk *TunnelFlowKey) SetCsum(csum bool) {
+ fk.key.Csum = csum
+ fk.mask.Csum = true
+}
+
+func (fk *TunnelFlowKey) SetTpSrc(port uint16) {
+ fk.key.TpSrc = port
+ fk.mask.TpSrc = 0xffff
+}
+
+func (fk *TunnelFlowKey) SetTpDst(port uint16) {
+ fk.key.TpDst = port
+ fk.mask.TpDst = 0xffff
+}
+
+func (key TunnelFlowKey) putKeyNlAttr(msg *NlMsgBuilder) {
+ msg.PutNestedAttrs(OVS_KEY_ATTR_TUNNEL, func() {
+ key.key.toNlAttrs(msg, key.mask.present())
+ })
+}
+
+func (key TunnelFlowKey) putMaskNlAttr(msg *NlMsgBuilder) error {
+ msg.PutNestedAttrs(OVS_KEY_ATTR_TUNNEL, func() {
+ key.mask.toNlAttrs(msg, key.mask.present())
+ })
+ return nil
+}
+
+func (a TunnelFlowKey) Equals(gb FlowKey) bool {
+ b, ok := gb.(TunnelFlowKey)
+ if !ok {
+ return false
+ }
+ return a.key == b.key && a.mask == b.mask
+}
+
+func (key TunnelFlowKey) Ignored() bool {
+ m := key.mask
+ return AllBytes(m.TunnelId[:], 0) &&
+ AllBytes(m.Ipv4Src[:], 0) &&
+ AllBytes(m.Ipv4Dst[:], 0) &&
+ m.Tos == 0 &&
+ m.Ttl == 0 &&
+ !m.Csum && !m.Csum &&
+ m.TpSrc == 0 && m.TpDst == 0
+}
+
+func parseTunnelFlowKey(typ uint16, key []byte, mask []byte, exact bool) (FlowKey, error) {
+ var k, m TunnelAttrs
+ var kp TunnelAttrsPresence
+ var err error
+
+ if key != nil {
+ k, kp, err = parseTunnelAttrs(key)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if mask != nil {
+ // We don't care about mask presence information,
+ // because a missing mask attribute means the field is
+ // wildcarded
+ m, _, err = parseTunnelAttrs(mask)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ // mask being nil means that no mask attributes were
+ // provided, which means the mask is implicit in the
+ // key attributes provided
+ m = kp.mask()
+ }
+
+ return TunnelFlowKey{key: k, mask: m}, err
+}
+
+var flowKeyParsers = FlowKeyParsers{
+ // Packet QoS priority flow key
+ OVS_KEY_ATTR_PRIORITY: blobFlowKeyParser(4, nil),
+
+ OVS_KEY_ATTR_IN_PORT: FlowKeyParser{
+ parse: parseInPortFlowKey,
+ exactMask: []byte{0xff, 0xff, 0xff, 0xff},
+ ignoreMask: []byte{0, 0, 0, 0},
+ },
+
+ OVS_KEY_ATTR_ETHERNET: ethernetFlowKeyParser,
+ OVS_KEY_ATTR_ETHERTYPE: blobFlowKeyParser(2, nil),
+ OVS_KEY_ATTR_IPV4: blobFlowKeyParser(12, nil),
+ OVS_KEY_ATTR_IPV6: blobFlowKeyParser(40, nil),
+ OVS_KEY_ATTR_TCP: blobFlowKeyParser(4, nil),
+ OVS_KEY_ATTR_UDP: blobFlowKeyParser(4, nil),
+ OVS_KEY_ATTR_ICMP: blobFlowKeyParser(2, nil),
+ OVS_KEY_ATTR_ICMPV6: blobFlowKeyParser(2, nil),
+ OVS_KEY_ATTR_ARP: blobFlowKeyParser(24, nil),
+ OVS_KEY_ATTR_ND: blobFlowKeyParser(28, nil),
+ OVS_KEY_ATTR_SKB_MARK: blobFlowKeyParser(4, nil),
+ OVS_KEY_ATTR_DP_HASH: blobFlowKeyParser(4, nil),
+ OVS_KEY_ATTR_TCP_FLAGS: blobFlowKeyParser(2, nil),
+ OVS_KEY_ATTR_RECIRC_ID: blobFlowKeyParser(4, nil),
+
+ OVS_KEY_ATTR_TUNNEL: FlowKeyParser{
+ parse: parseTunnelFlowKey,
+ exactMask: nil,
+ ignoreMask: []byte{},
+ },
+}
+
+func MakeFlowKeys() FlowKeys {
+ return make(FlowKeys)
+}
+
+func (keys FlowKeys) Add(k FlowKey) {
+ // TODO check for collisions
+ keys[k.typeId()] = k
+}
+
+// Actions
+
+type Action interface {
+ typeId() uint16
+ toNlAttr(*NlMsgBuilder)
+ Equals(Action) bool
+}
+
+type OutputAction VportID
+
+func NewOutputAction(vport VportID) OutputAction {
+ return OutputAction(vport)
+}
+
+func (oa OutputAction) String() string {
+ return fmt.Sprintf("OutputAction{vport: %d}", oa)
+}
+
+func (oa OutputAction) VportID() VportID {
+ return VportID(oa)
+}
+
+func (OutputAction) typeId() uint16 {
+ return OVS_ACTION_ATTR_OUTPUT
+}
+
+func (oa OutputAction) toNlAttr(msg *NlMsgBuilder) {
+ msg.PutUint32Attr(OVS_ACTION_ATTR_OUTPUT, uint32(oa))
+}
+
+func (a OutputAction) Equals(bx Action) bool {
+ b, ok := bx.(OutputAction)
+ if !ok {
+ return false
+ }
+ return a == b
+}
+
+func parseOutputAction(typ uint16, data []byte) (Action, error) {
+ if len(data) < 4 {
+ return nil, fmt.Errorf("flow action type %d has wrong length (expects 4 bytes, got %d)", typ, len(data))
+ }
+
+ return OutputAction(*uint32At(data, 0)), nil
+}
+
+type SetTunnelAction struct {
+ TunnelAttrs
+ Present TunnelAttrsPresence
+}
+
+func (ta SetTunnelAction) String() string {
+ var buf bytes.Buffer
+ var sep string
+ fmt.Fprint(&buf, "SetTunnelAction{")
+
+ if ta.Present.TunnelId {
+ fmt.Fprintf(&buf, "%sid: %s", sep,
+ hex.EncodeToString(ta.TunnelId[:]))
+ sep = ", "
+ }
+
+ if ta.Present.Ipv4Src {
+ fmt.Fprintf(&buf, "%sipv4src: %s", sep,
+ ipv4ToString(ta.Ipv4Src[:]))
+ sep = ", "
+ }
+
+ if ta.Present.Ipv4Dst {
+ fmt.Fprintf(&buf, "%sipv4dst: %s", sep,
+ ipv4ToString(ta.Ipv4Dst[:]))
+ sep = ", "
+ }
+
+ if ta.Present.Tos {
+ fmt.Fprintf(&buf, "%stos: %d", sep, ta.Tos)
+ sep = ", "
+ }
+
+ if ta.Present.Ttl {
+ fmt.Fprintf(&buf, "%sttl: %d", sep, ta.Ttl)
+ sep = ", "
+ }
+
+ if ta.Present.Df {
+ fmt.Fprintf(&buf, "%sdf: %t", sep, ta.Df)
+ sep = ", "
+ }
+
+ if ta.Present.Csum {
+ fmt.Fprintf(&buf, "%scsum: %t", sep, ta.Csum)
+ sep = ", "
+ }
+
+ if ta.Present.TpSrc {
+ fmt.Fprintf(&buf, "%stpsrc: %d", sep, ta.TpSrc)
+ sep = ", "
+ }
+
+ if ta.Present.TpDst {
+ fmt.Fprintf(&buf, "%stpdst: %d", sep, ta.TpDst)
+ sep = ", "
+ }
+
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+func (SetTunnelAction) typeId() uint16 {
+ return OVS_ACTION_ATTR_SET
+}
+
+func (ta SetTunnelAction) toNlAttr(msg *NlMsgBuilder) {
+ msg.PutNestedAttrs(OVS_ACTION_ATTR_SET, func() {
+ msg.PutNestedAttrs(OVS_KEY_ATTR_TUNNEL, func() {
+ ta.Present.Df = ta.Df
+ ta.Present.Csum = ta.Csum
+ ta.TunnelAttrs.toNlAttrs(msg, ta.Present)
+ })
+ })
+}
+
+func (a SetTunnelAction) Equals(bx Action) bool {
+ b, ok := bx.(SetTunnelAction)
+ if !ok {
+ return false
+ }
+ return a.TunnelAttrs == b.TunnelAttrs
+}
+
+func (a *SetTunnelAction) SetTunnelId(id [8]byte) {
+ a.TunnelId = id
+ a.Present.TunnelId = true
+}
+
+func (a *SetTunnelAction) SetIpv4Src(addr [4]byte) {
+ a.Ipv4Src = addr
+ a.Present.Ipv4Src = true
+}
+
+func (a *SetTunnelAction) SetIpv4Dst(addr [4]byte) {
+ a.Ipv4Dst = addr
+ a.Present.Ipv4Dst = true
+}
+
+func (a *SetTunnelAction) SetTos(tos uint8) {
+ a.Tos = tos
+ a.Present.Tos = true
+}
+
+func (a *SetTunnelAction) SetTtl(ttl uint8) {
+ a.Ttl = ttl
+ a.Present.Ttl = true
+}
+
+func (a *SetTunnelAction) SetDf(df bool) {
+ a.Df = df
+ a.Present.Df = true
+}
+
+func (a *SetTunnelAction) SetCsum(csum bool) {
+ a.Csum = csum
+ a.Present.Csum = true
+}
+
+func (a *SetTunnelAction) SetTpSrc(port uint16) {
+ a.TpSrc = port
+ a.Present.TpSrc = true
+}
+
+func (a *SetTunnelAction) SetTpDst(port uint16) {
+ a.TpDst = port
+ a.Present.TpDst = true
+}
+
+func parseSetAction(typ uint16, data []byte) (Action, error) {
+ attrs, err := ParseNestedAttrs(data)
+ if err != nil {
+ return nil, err
+ }
+
+ var res Action
+ first := true
+ for typ, data := range attrs {
+ if !first {
+ return nil, fmt.Errorf("multiple attributes within OVS_ACTION_ATTR_SET")
+ }
+
+ switch typ {
+ case OVS_KEY_ATTR_TUNNEL:
+ ta, present, err := parseTunnelAttrs(data)
+ if err != nil {
+ return nil, err
+ }
+ res = SetTunnelAction{TunnelAttrs: ta, Present: present}
+ break
+
+ default:
+ return nil, fmt.Errorf("unsupported OVS_ACTION_ATTR_SET attribute %d", typ)
+ }
+
+ first = false
+ }
+
+ return res, nil
+}
+
+var actionParsers = map[uint16](func(uint16, []byte) (Action, error)){
+ OVS_ACTION_ATTR_OUTPUT: parseOutputAction,
+ OVS_ACTION_ATTR_SET: parseSetAction,
+}
+
+// Complete flows
+
+type FlowSpec struct {
+ FlowKeys
+ Actions []Action
+}
+
+func NewFlowSpec() FlowSpec {
+ return FlowSpec{FlowKeys: make(FlowKeys), Actions: nil}
+}
+
+func (f FlowSpec) String() string {
+ var keys []FlowKey
+
+ for _, k := range f.FlowKeys {
+ keys = append(keys, k)
+ }
+
+ return fmt.Sprintf("FlowSpec{keys: %v, actions: %v}", keys, f.Actions)
+}
+
+func (f *FlowSpec) AddKey(k FlowKey) {
+ f.FlowKeys.Add(k)
+}
+
+func (f *FlowSpec) AddAction(a Action) {
+ f.Actions = append(f.Actions, a)
+}
+
+func (f *FlowSpec) AddActions(as []Action) {
+ f.Actions = append(f.Actions, as...)
+}
+
+func (f FlowSpec) toNlAttrs(msg *NlMsgBuilder) error {
+ if err := f.FlowKeys.toNlAttrs(msg); err != nil {
+ return err
+ }
+
+ msg.PutNestedAttrs(OVS_FLOW_ATTR_ACTIONS, func() {
+ for _, a := range f.Actions {
+ a.toNlAttr(msg)
+ }
+ })
+
+ return nil
+}
+
+func (a FlowSpec) Equals(b FlowSpec) bool {
+ if !a.FlowKeys.Equals(b.FlowKeys) {
+ return false
+ }
+ if len(a.Actions) != len(b.Actions) {
+ return false
+ }
+
+ for i := range a.Actions {
+ if !a.Actions[i].Equals(b.Actions[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (dp DatapathHandle) parseFlowMsg(msg *NlMsgParser) (Attrs, error) {
+ if err := dp.checkNlMsgHeaders(msg, FLOW, OVS_FLOW_CMD_NEW); err != nil {
+ return nil, err
+ }
+
+ return msg.TakeAttrs()
+}
+
+func parseFlowSpec(attrs Attrs) (f FlowSpec, err error) {
+ keys, err := attrs.GetNestedAttrs(OVS_FLOW_ATTR_KEY, false)
+ if err != nil {
+ return f, err
+ }
+
+ masks, err := attrs.GetNestedAttrs(OVS_FLOW_ATTR_MASK, true)
+ if err != nil {
+ return f, err
+ }
+
+ f.FlowKeys, err = ParseFlowKeys(keys, masks)
+ if err != nil {
+ return f, err
+ }
+
+ actattrs, err := attrs.GetOrderedAttrs(OVS_FLOW_ATTR_ACTIONS)
+ if err != nil {
+ return f, err
+ }
+
+ actions := make([]Action, 0)
+ for _, actattr := range actattrs {
+ parser, ok := actionParsers[actattr.typ]
+ if !ok {
+ return f, fmt.Errorf("unknown action type %d (value %v)", actattr.typ, actattr.val)
+ }
+
+ action, err := parser(actattr.typ, actattr.val)
+ if err != nil {
+ return f, err
+ }
+ actions = append(actions, action)
+ }
+
+ f.Actions = actions
+ return f, nil
+}
+
+func (dp DatapathHandle) CreateFlow(f FlowSpec) error {
+ dpif := dp.dpif
+
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[FLOW].id)
+ req.PutGenlMsghdr(OVS_FLOW_CMD_NEW, OVS_FLOW_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ if err := f.toNlAttrs(req); err != nil {
+ return err
+ }
+
+ _, err := dpif.sock.Request(req)
+ return err
+}
+
+func (dp DatapathHandle) DeleteFlow(fks FlowKeys) error {
+ dpif := dp.dpif
+
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[FLOW].id)
+ req.PutGenlMsghdr(OVS_FLOW_CMD_DEL, OVS_FLOW_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ if err := fks.toNlAttrs(req); err != nil {
+ return err
+ }
+
+ _, err := dpif.sock.Request(req)
+ return err
+}
+
+func (dp DatapathHandle) ClearFlow(f FlowSpec) error {
+ dpif := dp.dpif
+
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[FLOW].id)
+ req.PutGenlMsghdr(OVS_FLOW_CMD_SET, OVS_FLOW_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ if err := f.toNlAttrs(req); err != nil {
+ return err
+ }
+
+ req.PutEmptyAttr(OVS_FLOW_ATTR_CLEAR)
+
+ _, err := dpif.sock.Request(req)
+ return err
+}
+
+func IsNoSuchFlowError(err error) bool {
+ return err == NetlinkError(syscall.ENOENT)
+}
+
+type FlowInfo struct {
+ FlowSpec
+ Packets uint64
+ Bytes uint64
+ Used uint64
+}
+
+func parseFlowInfo(attrs Attrs) (fi FlowInfo, err error) {
+ fi.FlowSpec, err = parseFlowSpec(attrs)
+ if err != nil {
+ return
+ }
+
+ statsBytes, err := attrs.GetFixedBytes(OVS_FLOW_ATTR_STATS,
+ SizeofOvsFlowStats, true)
+ if err != nil {
+ return
+ }
+
+ if statsBytes != nil {
+ stats := ovsFlowStatsAt(statsBytes, 0)
+ fi.Packets = stats.NPackets
+ fi.Bytes = stats.NBytes
+ }
+
+ used, usedPresent, err := attrs.GetOptionalUint64(OVS_FLOW_ATTR_USED)
+ if err != nil {
+ return
+ } else if usedPresent {
+ fi.Used = used
+ }
+
+ return
+}
+
+func (dp DatapathHandle) EnumerateFlows() ([]FlowInfo, error) {
+ dpif := dp.dpif
+ res := make([]FlowInfo, 0)
+
+ req := NewNlMsgBuilder(DumpFlags, dpif.families[FLOW].id)
+ req.PutGenlMsghdr(OVS_FLOW_CMD_GET, OVS_FLOW_VERSION)
+ req.putOvsHeader(dp.ifindex)
+
+ consumer := func(resp *NlMsgParser) error {
+ attrs, err := dp.parseFlowMsg(resp)
+ if err != nil {
+ return err
+ }
+
+ fi, err := parseFlowInfo(attrs)
+ if err != nil {
+ return err
+ }
+
+ res = append(res, fi)
+ return nil
+ }
+
+ err := dpif.sock.RequestMulti(req, consumer)
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/genetlink.go b/vendor/github.com/weaveworks/go-odp/odp/genetlink.go
new file mode 100644
index 000000000..c15bff0be
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/genetlink.go
@@ -0,0 +1,101 @@
+package odp
+
+import (
+ "fmt"
+ "syscall"
+)
+
+type GenlFamily struct {
+ id uint16
+ mcGroups map[string]uint32
+}
+
+func (nlmsg *NlMsgBuilder) PutGenlMsghdr(cmd uint8, version uint8) *GenlMsghdr {
+ pos := nlmsg.AlignGrow(syscall.NLMSG_ALIGNTO, SizeofGenlMsghdr)
+ res := genlMsghdrAt(nlmsg.buf, pos)
+ res.Cmd = cmd
+ res.Version = version
+ return res
+}
+
+func (nlmsg *NlMsgParser) CheckGenlMsghdr(cmd int) (*GenlMsghdr, error) {
+ pos, err := nlmsg.AlignAdvance(syscall.NLMSG_ALIGNTO, SizeofGenlMsghdr)
+ if err != nil {
+ return nil, err
+ }
+
+ gh := genlMsghdrAt(nlmsg.data, pos)
+ if cmd >= 0 && gh.Cmd != uint8(cmd) {
+ return nil, fmt.Errorf("generic netlink response has wrong cmd (got %d, expected %d)", gh.Cmd, cmd)
+ }
+
+ // Deliberately ignore the version field in the genl header.
+ // It's unclear exactly what its meaning is, and how we should
+ // handle it. E.g., if the version is higher than we expect,
+ // should we still try to handle the message? It's unclear,
+ // but the fact that ODP bumped the kernel
+ // OVS_DATAPATH_VERSION from 1 to 2 while expecting existing
+ // userspace to keep working suggests that we should be
+ // libreral in what we accept.
+
+ return gh, nil
+}
+
+func (s *NetlinkSocket) LookupGenlFamily(name string) (family GenlFamily, err error) {
+ req := NewNlMsgBuilder(RequestFlags, GENL_ID_CTRL)
+
+ req.PutGenlMsghdr(CTRL_CMD_GETFAMILY, 0)
+ req.PutStringAttr(CTRL_ATTR_FAMILY_NAME, name)
+
+ resp, err := s.Request(req)
+ if err != nil {
+ return
+ }
+
+ _, err = resp.ExpectNlMsghdr(GENL_ID_CTRL)
+ if err != nil {
+ return
+ }
+
+ _, err = resp.CheckGenlMsghdr(CTRL_CMD_NEWFAMILY)
+ if err != nil {
+ return
+ }
+
+ attrs, err := resp.TakeAttrs()
+ if err != nil {
+ return
+ }
+
+ family.id, err = attrs.GetUint16(CTRL_ATTR_FAMILY_ID)
+ if err != nil {
+ return
+ }
+
+ mcGroupAttrs, err := attrs.GetNestedAttrs(CTRL_ATTR_MCAST_GROUPS, true)
+ if err != nil || mcGroupAttrs == nil {
+ return
+ }
+
+ family.mcGroups = make(map[string]uint32)
+ for _, data := range mcGroupAttrs {
+ groupAttrs, err := ParseNestedAttrs(data)
+ if err != nil {
+ return family, err
+ }
+
+ id, err := groupAttrs.GetUint32(CTRL_ATTR_MCAST_GRP_ID)
+ if err != nil {
+ return family, err
+ }
+
+ name, err := groupAttrs.GetString(CTRL_ATTR_MCAST_GRP_NAME)
+ if err != nil {
+ return family, err
+ }
+
+ family.mcGroups[name] = id
+ }
+
+ return
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/netlink.go b/vendor/github.com/weaveworks/go-odp/odp/netlink.go
new file mode 100644
index 000000000..d89fcad2e
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/netlink.go
@@ -0,0 +1,698 @@
+package odp
+
+import (
+ "fmt"
+ "reflect"
+ "sync/atomic"
+ "syscall"
+)
+
+func align(n int, a int) int {
+ return (n + a - 1) & -a
+}
+
+type NetlinkSocket struct {
+ fd int
+ addr *syscall.SockaddrNetlink
+ buf []byte
+}
+
+func OpenNetlinkSocket(protocol int) (*NetlinkSocket, error) {
+ fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
+ if err != nil {
+ return nil, err
+ }
+
+ success := false
+ defer func() {
+ if !success {
+ syscall.Close(fd)
+ }
+ }()
+
+ // It's fairly easy to provoke ENOBUFS from a netlink socket
+ // receiving miss upcalls when every packet misses. The
+ // default socket buffer size is relatively small at 200KB,
+ // and the default of /proc/sys/net/core/rmem_max means we
+ // can't easily increase it.
+ if err := syscall.SetsockoptInt(fd, SOL_NETLINK, syscall.NETLINK_NO_ENOBUFS, 1); err != nil {
+ return nil, err
+ }
+
+ addr := syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
+ if err := syscall.Bind(fd, &addr); err != nil {
+ return nil, err
+ }
+
+ localaddr, err := syscall.Getsockname(fd)
+ if err != nil {
+ return nil, err
+ }
+
+ nladdr, ok := localaddr.(*syscall.SockaddrNetlink)
+ if !ok {
+ return nil, fmt.Errorf("Expected netlink sockaddr, got %s", reflect.TypeOf(localaddr))
+ }
+
+ success = true
+ return &NetlinkSocket{
+ fd: fd,
+ addr: nladdr,
+
+ // netlink messages can be bigger than this, but it
+ // seems unlikely in practice, and this is similar to
+ // the limit that the OVS userspace imposes.
+ buf: make([]byte, 65536),
+ }, nil
+}
+
+func (s *NetlinkSocket) PortId() uint32 {
+ return s.addr.Pid
+}
+
+func (s *NetlinkSocket) Close() error {
+ if s.fd < 0 {
+ return nil
+ }
+
+ err := syscall.Close(s.fd)
+ s.fd = -1
+ return err
+}
+
+type NlMsgBuilder struct {
+ buf []byte
+}
+
+func NewNlMsgBuilder(flags uint16, typ uint16) *NlMsgBuilder {
+ buf := MakeAlignedByteSlice(syscall.NLMSG_HDRLEN)
+ nlmsg := &NlMsgBuilder{buf: buf}
+ h := nlMsghdrAt(buf, 0)
+ h.Flags = flags
+ h.Type = typ
+ return nlmsg
+}
+
+// Expand the array underlying a slice to have capacity of at least l
+func expand(buf []byte, l int) []byte {
+ c := (cap(buf) + 1) * 3 / 2
+ for l > c {
+ c = (c + 1) * 3 / 2
+ }
+ new := MakeAlignedByteSliceCap(len(buf), c)
+ copy(new, buf)
+ return new
+}
+
+func (nlmsg *NlMsgBuilder) Align(a int) {
+ l := align(len(nlmsg.buf), a)
+ if l > cap(nlmsg.buf) {
+ nlmsg.buf = expand(nlmsg.buf, l)
+ }
+ nlmsg.buf = nlmsg.buf[:l]
+}
+
+func (nlmsg *NlMsgBuilder) Grow(size uintptr) int {
+ pos := len(nlmsg.buf)
+ l := pos + int(size)
+ if l > cap(nlmsg.buf) {
+ nlmsg.buf = expand(nlmsg.buf, l)
+ }
+ nlmsg.buf = nlmsg.buf[:l]
+ return pos
+}
+
+func (nlmsg *NlMsgBuilder) AlignGrow(a int, size uintptr) int {
+ apos := align(len(nlmsg.buf), a)
+ l := apos + int(size)
+ if l > cap(nlmsg.buf) {
+ nlmsg.buf = expand(nlmsg.buf, l)
+ }
+ nlmsg.buf = nlmsg.buf[:l]
+ return apos
+}
+
+var nextSeqNo uint32
+
+func (nlmsg *NlMsgBuilder) Finish() (res []byte, seq uint32) {
+ h := nlMsghdrAt(nlmsg.buf, 0)
+ h.Len = uint32(len(nlmsg.buf))
+ seq = atomic.AddUint32(&nextSeqNo, 1)
+ h.Seq = seq
+ res = nlmsg.buf
+ nlmsg.buf = nil
+ return
+}
+
+func (nlmsg *NlMsgBuilder) PutAttr(typ uint16, gen func()) {
+ pos := nlmsg.AlignGrow(syscall.NLA_ALIGNTO, syscall.SizeofNlAttr)
+ gen()
+ nla := nlAttrAt(nlmsg.buf, pos)
+ nla.Type = typ
+ nla.Len = uint16(len(nlmsg.buf) - pos)
+}
+
+func (nlmsg *NlMsgBuilder) PutNestedAttrs(typ uint16, gen func()) {
+ nlmsg.PutAttr(typ, func() {
+ gen()
+
+ // The kernel nlattr parser expects the alignment
+ // padding at the end of a nested attributes value to
+ // be included in the length of the enclosing
+ // attribute
+ nlmsg.Align(syscall.NLA_ALIGNTO)
+ })
+}
+
+func (nlmsg *NlMsgBuilder) PutEmptyAttr(typ uint16) {
+ nlmsg.PutAttr(typ, func() {})
+}
+
+func (nlmsg *NlMsgBuilder) PutUint8Attr(typ uint16, val uint8) {
+ nlmsg.PutAttr(typ, func() {
+ pos := nlmsg.Grow(1)
+ nlmsg.buf[pos] = val
+ })
+}
+
+func (nlmsg *NlMsgBuilder) PutUint16Attr(typ uint16, val uint16) {
+ nlmsg.PutAttr(typ, func() {
+ pos := nlmsg.Grow(2)
+ *uint16At(nlmsg.buf, pos) = val
+ })
+}
+
+func (nlmsg *NlMsgBuilder) PutUint32Attr(typ uint16, val uint32) {
+ nlmsg.PutAttr(typ, func() {
+ pos := nlmsg.Grow(4)
+ *uint32At(nlmsg.buf, pos) = val
+ })
+}
+
+func (nlmsg *NlMsgBuilder) putStringZ(str string) {
+ l := len(str)
+ pos := nlmsg.Grow(uintptr(l) + 1)
+ copy(nlmsg.buf[pos:], str)
+ nlmsg.buf[pos+l] = 0
+}
+
+func (nlmsg *NlMsgBuilder) PutStringAttr(typ uint16, str string) {
+ nlmsg.PutAttr(typ, func() { nlmsg.putStringZ(str) })
+}
+
+func (nlmsg *NlMsgBuilder) PutSliceAttr(typ uint16, data []byte) {
+ nlmsg.PutAttr(typ, func() {
+ pos := nlmsg.Grow(uintptr(len(data)))
+ copy(nlmsg.buf[pos:], data)
+ })
+}
+
+type NetlinkError syscall.Errno
+
+func (err NetlinkError) Error() string {
+ return fmt.Sprintf("netlink error response: %s", syscall.Errno(err))
+}
+
+type NlMsgParser struct {
+ data []byte
+ pos int
+}
+
+func (nlmsg *NlMsgParser) Advance(size uintptr) error {
+ if err := nlmsg.CheckAvailable(size); err != nil {
+ return err
+ }
+
+ nlmsg.pos += int(size)
+ return nil
+}
+
+func (nlmsg *NlMsgParser) AlignAdvance(a int, size uintptr) (int, error) {
+ pos := align(nlmsg.pos, a)
+ nlmsg.pos = pos
+ if err := nlmsg.Advance(size); err != nil {
+ return 0, err
+ }
+
+ return pos, nil
+}
+
+func (nlmsg *NlMsgParser) NlMsghdr() *syscall.NlMsghdr {
+ return nlMsghdrAt(nlmsg.data, nlmsg.pos)
+}
+
+func (msg *NlMsgParser) nextNlMsg() (*NlMsgParser, error) {
+ pos := msg.pos
+ avail := len(msg.data) - pos
+ if avail <= 0 {
+ return nil, nil
+ }
+
+ if avail < syscall.SizeofNlMsghdr {
+ return nil, fmt.Errorf("netlink message header truncated")
+ }
+
+ h := msg.NlMsghdr()
+ if avail < int(h.Len) {
+ return nil, fmt.Errorf("netlink message truncated (%d bytes available, %d expected)", avail, h.Len)
+ }
+
+ end := pos + int(h.Len)
+ msg.pos = align(end, syscall.NLMSG_ALIGNTO)
+ return &NlMsgParser{data: msg.data[:end], pos: pos}, nil
+}
+
+func (nlmsg *NlMsgParser) CheckAvailable(size uintptr) error {
+ if nlmsg.pos+int(size) > len(nlmsg.data) {
+ return fmt.Errorf("netlink message truncated")
+ }
+
+ return nil
+}
+
+func (nlmsg *NlMsgParser) checkHeader() error {
+ // nextNlMsg ensures that there is an nlmsghdr-worth of data
+ // present
+ h := nlmsg.NlMsghdr()
+ if h.Type == syscall.NLMSG_ERROR {
+ nlerr := nlMsgerrAt(nlmsg.data, nlmsg.pos+syscall.NLMSG_HDRLEN)
+ if nlerr.Error != 0 {
+ return NetlinkError(-nlerr.Error)
+ }
+
+ // an error code of 0 means the error is an ack, so
+ // return normally.
+ }
+
+ return nil
+}
+
+func (nlmsg *NlMsgParser) checkResponseHeader(expectedPortId uint32, expectedSeq uint32) (relevant bool, err error) {
+ // nextNlMsg ensures that there is an nlmsghdr-worth of data
+ // present
+ h := nlmsg.NlMsghdr()
+ if h.Pid != expectedPortId {
+ return true, fmt.Errorf("netlink reply port id mismatch (got %d, expected %d)", h.Pid, expectedPortId)
+ }
+
+ if h.Seq != expectedSeq {
+ // This doesn't necessarily indicate an error. For
+ // example, if an early requestMulti was interrupted
+ // due to an error, we might still be getting its
+ // response messages back that, and we should discard
+ // them. On the other hand, sequence number
+ // mismatches might indicate bugs, so it is sometimes
+ // nice to see them in development.
+ fmt.Printf("netlink reply sequence number mismatch (got %d, expected %d)\n", h.Seq, expectedSeq)
+ return false, nil
+ }
+
+ return true, nlmsg.checkHeader()
+}
+
+func (nlmsg *NlMsgParser) ExpectNlMsghdr(typ uint16) (*syscall.NlMsghdr, error) {
+ h := nlmsg.NlMsghdr()
+
+ if err := nlmsg.Advance(syscall.SizeofNlMsghdr); err != nil {
+ return nil, err
+ }
+
+ if h.Type != typ {
+ return nil, fmt.Errorf("netlink response has wrong type (got %d, expected %d)", h.Type, typ)
+ }
+
+ return h, nil
+}
+
+type Attrs map[uint16][]byte
+
+func (attrs Attrs) Get(typ uint16, optional bool) ([]byte, error) {
+ val, ok := attrs[typ]
+ if !ok && !optional {
+ return nil, fmt.Errorf("missing netlink attribute %d", typ)
+ }
+
+ return val, nil
+}
+
+func (attrs Attrs) GetFixedBytes(typ uint16, expect int, optional bool) ([]byte, error) {
+ val, err := attrs.Get(typ, optional)
+ if err != nil || val == nil {
+ return nil, err
+ }
+
+ if len(val) != expect {
+ return nil, fmt.Errorf("attribute %d has wrong length (got %d bytes, expected %d bytes)", typ, len(val), expect)
+ }
+
+ return val, nil
+}
+
+func (attrs Attrs) GetOptionalBytes(typ uint16, dest []byte) (bool, error) {
+ val, err := attrs.GetFixedBytes(typ, len(dest), true)
+ if err != nil || val == nil {
+ return false, err
+ }
+
+ copy(dest, val)
+ return true, nil
+}
+
+func (attrs Attrs) GetEmpty(typ uint16) (bool, error) {
+ val, err := attrs.Get(typ, true)
+ if err != nil || val == nil {
+ return false, err
+ }
+
+ if len(val) != 0 {
+ return false, fmt.Errorf("empty attribute %d has wrong length (%d bytes)", typ, len(val))
+ }
+
+ return true, nil
+}
+
+func (attrs Attrs) GetOptionalUint8(typ uint16) (uint8, bool, error) {
+ val, err := attrs.Get(typ, true)
+ if err != nil || val == nil {
+ return 0, false, err
+ }
+
+ if len(val) != 1 {
+ return 0, false, fmt.Errorf("uint8 attribute %d has wrong length (%d bytes)", typ, len(val))
+ }
+
+ return val[0], true, nil
+}
+
+func (attrs Attrs) getUint16(typ uint16, optional bool) (uint16, bool, error) {
+ val, err := attrs.Get(typ, optional)
+ if err != nil || val == nil {
+ return 0, false, err
+ }
+
+ if len(val) != 2 {
+ return 0, false, fmt.Errorf("uint16 attribute %d has wrong length (%d bytes)", typ, len(val))
+ }
+
+ return *uint16At(val, 0), true, nil
+}
+
+func (attrs Attrs) GetUint16(typ uint16) (uint16, error) {
+ res, _, err := attrs.getUint16(typ, false)
+ return res, err
+}
+
+func (attrs Attrs) GetOptionalUint16(typ uint16) (uint16, bool, error) {
+ return attrs.getUint16(typ, true)
+}
+
+func (attrs Attrs) GetUint32(typ uint16) (uint32, error) {
+ val, err := attrs.Get(typ, false)
+ if err != nil {
+ return 0, err
+ }
+
+ if len(val) != 4 {
+ return 0, fmt.Errorf("uint32 attribute %d has wrong length (%d bytes)", typ, len(val))
+ }
+
+ return *uint32At(val, 0), nil
+}
+
+func (attrs Attrs) getUint64(typ uint16, optional bool) (uint64, bool, error) {
+ val, err := attrs.Get(typ, optional)
+ if err != nil || val == nil {
+ return 0, false, err
+ }
+
+ if len(val) != 8 {
+ return 0, false, fmt.Errorf("uint64 attribute %d has wrong length (%d bytes)", typ, len(val))
+ }
+
+ return *uint64At(val, 0), true, nil
+}
+
+func (attrs Attrs) GetUint64(typ uint16) (uint64, error) {
+ res, _, err := attrs.getUint64(typ, false)
+ return res, err
+}
+
+func (attrs Attrs) GetOptionalUint64(typ uint16) (uint64, bool, error) {
+ return attrs.getUint64(typ, true)
+}
+
+func (attrs Attrs) GetString(typ uint16) (string, error) {
+ val, err := attrs.Get(typ, false)
+ if err != nil {
+ return "", err
+ }
+
+ if len(val) == 0 {
+ return "", fmt.Errorf("string attribute %d has zero length", typ)
+ }
+
+ if val[len(val)-1] != 0 {
+ return "", fmt.Errorf("string attribute %d does not end with nul byte", typ)
+ }
+
+ return string(val[0 : len(val)-1]), nil
+}
+
+func (nlmsg *NlMsgParser) checkData(l uintptr, obj string) error {
+ if nlmsg.pos+int(l) <= len(nlmsg.data) {
+ return nil
+ } else {
+ return fmt.Errorf("truncated %s (have %d bytes, expected %d)", obj, len(nlmsg.data)-nlmsg.pos, l)
+ }
+}
+
+func (nlmsg *NlMsgParser) parseAttrs(consumer func(uint16, []byte)) error {
+ for {
+ apos := align(nlmsg.pos, syscall.NLA_ALIGNTO)
+ if len(nlmsg.data) <= apos {
+ break
+ }
+
+ nlmsg.pos = apos
+
+ if err := nlmsg.checkData(syscall.SizeofNlAttr, "netlink attribute"); err != nil {
+ return err
+ }
+
+ nla := nlAttrAt(nlmsg.data, nlmsg.pos)
+ if err := nlmsg.checkData(uintptr(nla.Len), "netlink attribute"); err != nil {
+ return err
+ }
+
+ valpos := align(nlmsg.pos+syscall.SizeofNlAttr, syscall.NLA_ALIGNTO)
+ consumer(nla.Type, nlmsg.data[valpos:nlmsg.pos+int(nla.Len)])
+ nlmsg.pos += int(nla.Len)
+ }
+
+ return nil
+}
+
+func (nlmsg *NlMsgParser) TakeAttrs() (Attrs, error) {
+ res := make(Attrs)
+ err := nlmsg.parseAttrs(func(typ uint16, val []byte) {
+ res[typ] = val
+ })
+ return res, err
+}
+
+func ParseNestedAttrs(data []byte) (Attrs, error) {
+ parser := NlMsgParser{data: data, pos: 0}
+ return parser.TakeAttrs()
+}
+
+func (attrs Attrs) GetNestedAttrs(typ uint16, optional bool) (Attrs, error) {
+ val, err := attrs.Get(typ, optional)
+ if val == nil {
+ return nil, err
+ }
+
+ return ParseNestedAttrs(val)
+}
+
+// Usually we parse attributes into a map, but there are cases where
+// attribute order matters.
+
+type Attr struct {
+ typ uint16
+ val []byte
+}
+
+func (attrs Attrs) GetOrderedAttrs(typ uint16) ([]Attr, error) {
+ val, err := attrs.Get(typ, false)
+ if val == nil {
+ return nil, err
+ }
+
+ parser := NlMsgParser{data: val, pos: 0}
+ res := make([]Attr, 0)
+ err = parser.parseAttrs(func(typ uint16, val []byte) {
+ res = append(res, Attr{typ, val})
+ })
+
+ return res, err
+}
+
+func (s *NetlinkSocket) send(msg *NlMsgBuilder) (uint32, error) {
+ sa := syscall.SockaddrNetlink{
+ Family: syscall.AF_NETLINK,
+ Pid: 0,
+ Groups: 0,
+ }
+
+ data, seq := msg.Finish()
+ return seq, syscall.Sendto(s.fd, data, 0, &sa)
+}
+
+func (s *NetlinkSocket) recv(peer uint32) (*NlMsgParser, error) {
+ nr, from, err := syscall.Recvfrom(s.fd, s.buf, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ buf := MakeAlignedByteSlice(nr)
+ copy(buf, s.buf)
+
+ switch nlfrom := from.(type) {
+ case *syscall.SockaddrNetlink:
+ if nlfrom.Pid != peer {
+ return nil, fmt.Errorf("wrong netlink peer pid (expected %d, got %d)", peer, nlfrom.Pid)
+ }
+
+ return &NlMsgParser{data: buf, pos: 0}, nil
+
+ default:
+ return nil, fmt.Errorf("Expected netlink sockaddr, got %s", reflect.TypeOf(from))
+ }
+}
+
+func (s *NetlinkSocket) Receive(consumer func(*NlMsgParser) (bool, error)) error {
+ for {
+ resp, err := s.recv(0)
+ if err != nil {
+ return err
+ }
+
+ msg, err := resp.nextNlMsg()
+ if err != nil {
+ return err
+ }
+ if msg == nil {
+ return fmt.Errorf("netlink response message missing")
+ }
+
+ for {
+ done, err := consumer(msg)
+ if done || err != nil {
+ return err
+ }
+
+ msg, err = resp.nextNlMsg()
+ if err != nil {
+ return err
+ }
+ if msg == nil {
+ break
+ }
+ }
+ }
+}
+
+// Some generic netlink operations always return a reply message (e.g
+// *_GET), others don't by default (e.g. *_NEW). In the latter case,
+// NLM_F_ECHO forces a reply. This is undocumented AFAICT.
+const RequestFlags = syscall.NLM_F_REQUEST | syscall.NLM_F_ECHO
+
+// Do a netlink request that yields a single response message.
+func (s *NetlinkSocket) Request(req *NlMsgBuilder) (resp *NlMsgParser, err error) {
+ seq, err := s.send(req)
+ if err != nil {
+ return nil, err
+ }
+
+ err = s.Receive(func(msg *NlMsgParser) (bool, error) {
+ relevant, err := msg.checkResponseHeader(s.PortId(), seq)
+ if relevant && err == nil {
+ resp = msg
+ }
+ return true, err
+ })
+ return
+}
+
+const DumpFlags = syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST
+
+// Do a netlink request that yield multiple response messages.
+func (s *NetlinkSocket) RequestMulti(req *NlMsgBuilder, consumer func(*NlMsgParser) error) error {
+ seq, err := s.send(req)
+ if err != nil {
+ return err
+ }
+
+ return s.Receive(func(msg *NlMsgParser) (bool, error) {
+ relevant, err := msg.checkResponseHeader(s.PortId(), seq)
+ if !relevant || err != nil {
+ return false, err
+ }
+
+ if msg.NlMsghdr().Type == syscall.NLMSG_DONE {
+ return true, processNlMsgDone(msg)
+ }
+
+ err = consumer(msg)
+ if err != nil {
+ return true, err
+ }
+
+ return false, nil
+ })
+}
+
+func processNlMsgDone(msg *NlMsgParser) error {
+ err := msg.Advance(syscall.SizeofNlMsghdr)
+ if err != nil {
+ return err
+ }
+
+ err = msg.checkData(4, "NLMSG_DONE error code")
+ if err != nil {
+ return err
+ }
+
+ errno := *int32At(msg.data, msg.pos)
+ if errno == 0 {
+ return nil
+ } else {
+ return NetlinkError(-errno)
+ }
+}
+
+type Consumer interface {
+ Error(err error, stopped bool)
+}
+
+func (s *NetlinkSocket) consume(consumer Consumer, handler func(*NlMsgParser) error) {
+ for {
+ err := s.Receive(func(msg *NlMsgParser) (bool, error) {
+ err := msg.checkHeader()
+ if err == nil {
+ err = handler(msg)
+ if err == nil {
+ return false, nil
+ }
+ }
+
+ consumer.Error(err, false)
+ return false, nil
+ })
+
+ if err != nil {
+ consumer.Error(err, true)
+ break
+ }
+ }
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/packet.go b/vendor/github.com/weaveworks/go-odp/odp/packet.go
new file mode 100644
index 000000000..5890f2a0d
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/packet.go
@@ -0,0 +1,173 @@
+package odp
+
+import (
+ "sync"
+)
+
+type MissConsumer interface {
+ Miss(packet []byte, flowKeys FlowKeys) error
+ Error(err error, stopped bool)
+}
+
+func (origDP DatapathHandle) ConsumeMisses(consumer MissConsumer) (Cancelable, error) {
+ // We end up needing 3 netlink sockets: one to consume
+ // misses, one to consume vport events, and one for general
+ // use.
+ dp, err := origDP.Reopen()
+ if err != nil {
+ return nil, err
+ }
+
+ success := false
+ defer func() {
+ if !success {
+ dp.dpif.Close()
+ }
+ }()
+
+ missDP, err := origDP.Reopen()
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if !success {
+ missDP.dpif.Close()
+ }
+ }()
+
+ // We need to set the upcall port ID on all vports. That
+ // includes vports that get added while we are listening, so
+ // we need to listen for them too.
+ vportConsumer := &missVportConsumer{
+ dp: dp,
+ upcallPortId: missDP.dpif.sock.PortId(),
+ missConsumer: consumer,
+ vportsDone: make(map[VportID]struct{}),
+ }
+
+ vportCancel, err := origDP.ConsumeVportEvents(vportConsumer)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if !success {
+ vportCancel.Cancel()
+ }
+ }()
+
+ vports, err := origDP.EnumerateVports()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, vport := range vports {
+ err = vportConsumer.setVportUpcallPortId(vport.ID)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ success = true
+ vportConsumer.cancel = vportCancel
+ go missDP.consumeMisses(consumer, vportConsumer)
+ return cancelableDpif{missDP.dpif}, nil
+}
+
+type missVportConsumer struct {
+ dp DatapathHandle
+ upcallPortId uint32
+ missConsumer MissConsumer
+ cancel Cancelable
+
+ lock sync.Mutex
+ vportsDone map[VportID]struct{}
+}
+
+// Set a vport's upcall port ID. This generates a OVS_VPORT_CMD_NEW
+// (not a OVS_VPORT_CMD_SET), leading to a call of the New method
+// below. So we need to record which vports we already processed in
+// order to avoid a vicious circle.
+func (c *missVportConsumer) setVportUpcallPortId(vport VportID) error {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if _, doneAlready := c.vportsDone[vport]; doneAlready {
+ return nil
+ }
+
+ if err := c.dp.setVportUpcallPortId(vport, c.upcallPortId); err != nil {
+ return err
+ }
+
+ c.vportsDone[vport] = struct{}{}
+ return nil
+}
+
+func (c *missVportConsumer) VportCreated(dpid DatapathID, vport Vport) error {
+ return c.setVportUpcallPortId(vport.ID)
+}
+
+func (c *missVportConsumer) VportDeleted(dpid DatapathID, vport Vport) error {
+ c.lock.Lock()
+ delete(c.vportsDone, vport.ID)
+ c.lock.Unlock()
+ return nil
+}
+
+func (c *missVportConsumer) Error(err error, stopped bool) {
+ c.missConsumer.Error(err, stopped)
+}
+
+func (dp DatapathHandle) consumeMisses(consumer MissConsumer, vportConsumer *missVportConsumer) {
+ dp.dpif.sock.consume(consumer, func(msg *NlMsgParser) error {
+ if err := dp.checkNlMsgHeaders(msg, PACKET, OVS_PACKET_CMD_MISS); err != nil {
+ return err
+ }
+
+ attrs, err := msg.TakeAttrs()
+ if err != nil {
+ return err
+ }
+
+ fkattrs, err := attrs.GetNestedAttrs(OVS_PACKET_ATTR_KEY, false)
+ if err != nil {
+ return err
+ }
+
+ fks, err := ParseFlowKeys(fkattrs, nil)
+ if err != nil {
+ return err
+ }
+
+ return consumer.Miss(attrs[OVS_PACKET_ATTR_PACKET], fks)
+ })
+
+ vportConsumer.cancel.Cancel()
+ vportConsumer.dp.dpif.Close()
+}
+
+func (dp DatapathHandle) Execute(packet []byte, keys FlowKeys, actions []Action) error {
+ dpif := dp.dpif
+
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[PACKET].id)
+ req.PutGenlMsghdr(OVS_PACKET_CMD_EXECUTE, OVS_PACKET_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ req.PutSliceAttr(OVS_PACKET_ATTR_PACKET, packet)
+
+ req.PutNestedAttrs(OVS_PACKET_ATTR_KEY, func() {
+ for _, k := range keys {
+ k.putKeyNlAttr(req)
+ }
+ })
+
+ req.PutNestedAttrs(OVS_PACKET_ATTR_ACTIONS, func() {
+ for _, a := range actions {
+ a.toNlAttr(req)
+ }
+ })
+
+ _, err := dpif.sock.send(req)
+ return err
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/syscall.go b/vendor/github.com/weaveworks/go-odp/odp/syscall.go
new file mode 100644
index 000000000..b85a5f3f0
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/syscall.go
@@ -0,0 +1,220 @@
+package odp
+
+import "syscall"
+
+// from linux/include/linux/socket.h
+const SOL_NETLINK = 270
+
+type GenlMsghdr struct {
+ Cmd uint8
+ Version uint8
+ Reserved uint16
+}
+
+const SizeofGenlMsghdr = 4
+
+// reserved static generic netlink identifiers:
+const (
+ GENL_ID_GENERATE = 0
+ GENL_ID_CTRL = syscall.NLMSG_MIN_TYPE
+ GENL_ID_VFS_DQUOT = syscall.NLMSG_MIN_TYPE + 1
+ GENL_ID_PMCRAID = syscall.NLMSG_MIN_TYPE + 2
+)
+
+const (
+ CTRL_CMD_UNSPEC = 0
+ CTRL_CMD_NEWFAMILY = 1
+ CTRL_CMD_DELFAMILY = 2
+ CTRL_CMD_GETFAMILY = 3
+ CTRL_CMD_NEWOPS = 4
+ CTRL_CMD_DELOPS = 5
+ CTRL_CMD_GETOPS = 6
+ CTRL_CMD_NEWMCAST_GRP = 7
+ CTRL_CMD_DELMCAST_GRP = 8
+)
+
+const (
+ CTRL_ATTR_UNSPEC = 0
+ CTRL_ATTR_FAMILY_ID = 1
+ CTRL_ATTR_FAMILY_NAME = 2
+ CTRL_ATTR_VERSION = 3
+ CTRL_ATTR_HDRSIZE = 4
+ CTRL_ATTR_MAXATTR = 5
+ CTRL_ATTR_OPS = 6
+ CTRL_ATTR_MCAST_GROUPS = 7
+)
+
+const (
+ CTRL_ATTR_MCAST_GRP_UNSPEC = 0
+ CTRL_ATTR_MCAST_GRP_NAME = 1
+ CTRL_ATTR_MCAST_GRP_ID = 2
+)
+
+type OvsHeader struct {
+ DpIfIndex int32
+}
+
+const SizeofOvsHeader = 4
+
+const (
+ OVS_DATAPATH_VERSION = 2
+ OVS_VPORT_VERSION = 1
+ OVS_FLOW_VERSION = 1
+ OVS_PACKET_VERSION = 1
+)
+
+const ( // ovs_datapath_cmd
+ OVS_DP_CMD_UNSPEC = 0
+ OVS_DP_CMD_NEW = 1
+ OVS_DP_CMD_DEL = 2
+ OVS_DP_CMD_GET = 3
+ OVS_DP_CMD_SET = 4
+)
+
+const ( // ovs_datapath_attr
+ OVS_DP_ATTR_UNSPEC = 0
+ OVS_DP_ATTR_NAME = 1
+ OVS_DP_ATTR_UPCALL_PID = 2
+ OVS_DP_ATTR_STATS = 3
+ OVS_DP_ATTR_MEGAFLOW_STATS = 4
+ OVS_DP_ATTR_USER_FEATURES = 5
+)
+
+const (
+ OVS_DP_F_UNALIGNED = 1
+ OVS_DP_F_VPORT_PIDS = 2
+)
+
+const ( // ovs_vport_cmd
+ OVS_VPORT_CMD_UNSPEC = 0
+ OVS_VPORT_CMD_NEW = 1
+ OVS_VPORT_CMD_DEL = 2
+ OVS_VPORT_CMD_GET = 3
+ OVS_VPORT_CMD_SET = 4
+)
+
+const ( // ovs_vport_attr
+ OVS_VPORT_ATTR_UNSPEC = 0
+ OVS_VPORT_ATTR_PORT_NO = 1
+ OVS_VPORT_ATTR_TYPE = 2
+ OVS_VPORT_ATTR_NAME = 3
+ OVS_VPORT_ATTR_OPTIONS = 4
+ OVS_VPORT_ATTR_UPCALL_PID = 5
+ OVS_VPORT_ATTR_STATS = 6
+)
+
+const ( // ovs_vport_type
+ OVS_VPORT_TYPE_UNSPEC = 0
+ OVS_VPORT_TYPE_NETDEV = 1
+ OVS_VPORT_TYPE_INTERNAL = 2
+ OVS_VPORT_TYPE_GRE = 3
+ OVS_VPORT_TYPE_VXLAN = 4
+)
+
+const ( // OVS_VPORT_ATTR_OPTIONS attributes for tunnels
+ OVS_TUNNEL_ATTR_UNSPEC = 0
+ OVS_TUNNEL_ATTR_DST_PORT = 1
+)
+
+const ( // ovs_flow_cmd
+ OVS_FLOW_CMD_UNSPEC = 0
+ OVS_FLOW_CMD_NEW = 1
+ OVS_FLOW_CMD_DEL = 2
+ OVS_FLOW_CMD_GET = 3
+ OVS_FLOW_CMD_SET = 4
+)
+
+const ( // ovs_flow_attr
+ OVS_FLOW_ATTR_UNSPEC = 0
+ OVS_FLOW_ATTR_KEY = 1
+ OVS_FLOW_ATTR_ACTIONS = 2
+ OVS_FLOW_ATTR_STATS = 3
+ OVS_FLOW_ATTR_TCP_FLAGS = 4
+ OVS_FLOW_ATTR_USED = 5
+ OVS_FLOW_ATTR_CLEAR = 6
+ OVS_FLOW_ATTR_MASK = 7
+)
+
+type OvsFlowStats struct {
+ NPackets uint64
+ NBytes uint64
+}
+
+const SizeofOvsFlowStats = 16
+
+const ( // ovs_key_attr
+ OVS_KEY_ATTR_UNSPEC = 0
+ OVS_KEY_ATTR_ENCAP = 1
+ OVS_KEY_ATTR_PRIORITY = 2
+ OVS_KEY_ATTR_IN_PORT = 3
+ OVS_KEY_ATTR_ETHERNET = 4
+ OVS_KEY_ATTR_VLAN = 5
+ OVS_KEY_ATTR_ETHERTYPE = 6
+ OVS_KEY_ATTR_IPV4 = 7
+ OVS_KEY_ATTR_IPV6 = 8
+ OVS_KEY_ATTR_TCP = 9
+ OVS_KEY_ATTR_UDP = 10
+ OVS_KEY_ATTR_ICMP = 11
+ OVS_KEY_ATTR_ICMPV6 = 12
+ OVS_KEY_ATTR_ARP = 13
+ OVS_KEY_ATTR_ND = 14
+ OVS_KEY_ATTR_SKB_MARK = 15
+ OVS_KEY_ATTR_TUNNEL = 16
+ OVS_KEY_ATTR_SCTP = 17
+ OVS_KEY_ATTR_TCP_FLAGS = 18
+ OVS_KEY_ATTR_DP_HASH = 19
+ OVS_KEY_ATTR_RECIRC_ID = 20
+)
+
+const ( // ovs_tunnel_key_attr
+ OVS_TUNNEL_KEY_ATTR_ID = 0
+ OVS_TUNNEL_KEY_ATTR_IPV4_SRC = 1
+ OVS_TUNNEL_KEY_ATTR_IPV4_DST = 2
+ OVS_TUNNEL_KEY_ATTR_TOS = 3
+ OVS_TUNNEL_KEY_ATTR_TTL = 4
+ OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT = 5
+ OVS_TUNNEL_KEY_ATTR_CSUM = 6
+ OVS_TUNNEL_KEY_ATTR_OAM = 7
+ OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS = 8
+ OVS_TUNNEL_KEY_ATTR_TP_SRC = 9
+ OVS_TUNNEL_KEY_ATTR_TP_DST = 10
+)
+
+const ETH_ALEN = 6
+
+type OvsKeyEthernet struct {
+ EthSrc [ETH_ALEN]byte
+ EthDst [ETH_ALEN]byte
+}
+
+const SizeofOvsKeyEthernet = 12
+
+const ( // ovs_action_attr
+ OVS_ACTION_ATTR_UNSPEC = 0
+ OVS_ACTION_ATTR_OUTPUT = 1
+ OVS_ACTION_ATTR_USERSPACE = 2
+ OVS_ACTION_ATTR_SET = 3
+ OVS_ACTION_ATTR_PUSH_VLAN = 4
+ OVS_ACTION_ATTR_POP_VLAN = 5
+ OVS_ACTION_ATTR_SAMPLE = 6
+)
+
+const ( // ovs_packet_cmd
+ OVS_PACKET_CMD_UNSPEC = 0
+ OVS_PACKET_CMD_MISS = 1
+ OVS_PACKET_CMD_ACTION = 2
+ OVS_PACKET_CMD_EXECUTE = 3
+)
+
+const ( // ovs_packet_attr
+ OVS_PACKET_ATTR_UNSPEC = 0
+ OVS_PACKET_ATTR_PACKET = 1
+ OVS_PACKET_ATTR_KEY = 2
+ OVS_PACKET_ATTR_ACTIONS = 3
+ OVS_PACKET_ATTR_USERDATA = 4
+)
+
+type ifreqIfindex struct {
+ name [syscall.IFNAMSIZ]byte
+ ifindex int32
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/unsafe.go b/vendor/github.com/weaveworks/go-odp/odp/unsafe.go
new file mode 100644
index 000000000..1bece3207
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/unsafe.go
@@ -0,0 +1,82 @@
+package odp
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const ALIGN_BUFFERS = 8
+
+// Normal slice or array allocations in golang do not appear to be
+// guaranteed to be aligned (though in practice they are). Unaligned
+// access are slow on some architectures and blow up on others. So
+// this allocates a slice aligned to ALIGN_BUFFERS.
+func MakeAlignedByteSliceCap(len int, cap int) []byte {
+ b := make([]byte, cap+ALIGN_BUFFERS-1)
+ off := int(uintptr(unsafe.Pointer(&b[0])) & (ALIGN_BUFFERS - 1))
+ if off == 0 {
+ // Already aligned
+ return b[:len]
+ } else {
+ // Need to offset the slice to make it aligned
+ off = ALIGN_BUFFERS - off
+ return b[off : len+off]
+ }
+}
+
+func MakeAlignedByteSlice(len int) []byte {
+ return MakeAlignedByteSliceCap(len, len)
+}
+
+func uint16At(data []byte, pos int) *uint16 {
+ return (*uint16)(unsafe.Pointer(&data[pos]))
+}
+
+func uint32At(data []byte, pos int) *uint32 {
+ return (*uint32)(unsafe.Pointer(&data[pos]))
+}
+
+func int32At(data []byte, pos int) *int32 {
+ return (*int32)(unsafe.Pointer(&data[pos]))
+}
+
+func uint64At(data []byte, pos int) *uint64 {
+ return (*uint64)(unsafe.Pointer(&data[pos]))
+}
+
+func nlMsghdrAt(data []byte, pos int) *syscall.NlMsghdr {
+ return (*syscall.NlMsghdr)(unsafe.Pointer(&data[pos]))
+}
+
+func nlAttrAt(data []byte, pos int) *syscall.NlAttr {
+ return (*syscall.NlAttr)(unsafe.Pointer(&data[pos]))
+}
+
+func nlMsgerrAt(data []byte, pos int) *syscall.NlMsgerr {
+ return (*syscall.NlMsgerr)(unsafe.Pointer(&data[pos]))
+}
+
+func genlMsghdrAt(data []byte, pos int) *GenlMsghdr {
+ return (*GenlMsghdr)(unsafe.Pointer(&data[pos]))
+}
+
+func ovsHeaderAt(data []byte, pos int) *OvsHeader {
+ return (*OvsHeader)(unsafe.Pointer(&data[pos]))
+}
+
+func ovsKeyEthernetAt(data []byte, pos int) *OvsKeyEthernet {
+ return (*OvsKeyEthernet)(unsafe.Pointer(&data[pos]))
+}
+
+func ovsFlowStatsAt(data []byte, pos int) *OvsFlowStats {
+ return (*OvsFlowStats)(unsafe.Pointer(&data[pos]))
+}
+
+func uint16FromBE(n uint16) uint16 {
+ a := (*[2]byte)(unsafe.Pointer(&n))
+ return uint16(a[0])<<8 + uint16(a[1])
+}
+
+func uint16ToBE(n uint16) uint16 {
+ return uint16FromBE(n)
+}
diff --git a/vendor/github.com/weaveworks/go-odp/odp/vport.go b/vendor/github.com/weaveworks/go-odp/odp/vport.go
new file mode 100644
index 000000000..a44229c5a
--- /dev/null
+++ b/vendor/github.com/weaveworks/go-odp/odp/vport.go
@@ -0,0 +1,362 @@
+package odp
+
+import (
+ "fmt"
+ "syscall"
+)
+
+type VportSpec interface {
+ TypeName() string
+ Name() string
+ typeId() uint32
+ optionNlAttrs(req *NlMsgBuilder)
+}
+
+type VportSpecBase struct {
+ name string
+}
+
+func (v VportSpecBase) Name() string {
+ return v.name
+}
+
+type SimpleVportSpec struct {
+ VportSpecBase
+ typ uint32
+ typeName string
+}
+
+func (s SimpleVportSpec) TypeName() string {
+ return s.typeName
+}
+
+func (s SimpleVportSpec) typeId() uint32 {
+ return s.typ
+}
+
+func (SimpleVportSpec) optionNlAttrs(req *NlMsgBuilder) {
+}
+
+func NewNetdevVportSpec(name string) VportSpec {
+ return SimpleVportSpec{
+ VportSpecBase{name},
+ OVS_VPORT_TYPE_NETDEV,
+ "netdev",
+ }
+}
+
+func NewInternalVportSpec(name string) VportSpec {
+ return SimpleVportSpec{
+ VportSpecBase{name},
+ OVS_VPORT_TYPE_INTERNAL,
+ "internal",
+ }
+}
+
+type VxlanVportSpec struct {
+ VportSpecBase
+ Port uint16
+}
+
+func (VxlanVportSpec) TypeName() string {
+ return "vxlan"
+}
+
+func (VxlanVportSpec) typeId() uint32 {
+ return OVS_VPORT_TYPE_VXLAN
+}
+
+func (v VxlanVportSpec) optionNlAttrs(req *NlMsgBuilder) {
+ req.PutUint16Attr(OVS_TUNNEL_ATTR_DST_PORT, v.Port)
+}
+
+func NewVxlanVportSpec(name string, port uint16) VportSpec {
+ return VxlanVportSpec{VportSpecBase{name}, port}
+}
+
+func parseVxlanVportSpec(name string, opts Attrs) (VportSpec, error) {
+ port, err := opts.GetUint16(OVS_TUNNEL_ATTR_DST_PORT)
+ if err != nil {
+ return nil, err
+ }
+
+ return VxlanVportSpec{VportSpecBase{name}, port}, nil
+}
+
+// Vport numbers are scoped to a particular datapath
+type VportID uint32
+
+func parseVport(msg *NlMsgParser) (id VportID, s VportSpec, err error) {
+ attrs, err := msg.TakeAttrs()
+ if err != nil {
+ return
+ }
+
+ rawid, err := attrs.GetUint32(OVS_VPORT_ATTR_PORT_NO)
+ if err != nil {
+ return
+ }
+
+ id = VportID(rawid)
+
+ typ, err := attrs.GetUint32(OVS_VPORT_ATTR_TYPE)
+ if err != nil {
+ return
+ }
+
+ name, err := attrs.GetString(OVS_VPORT_ATTR_NAME)
+ if err != nil {
+ return
+ }
+
+ opts, err := attrs.GetNestedAttrs(OVS_VPORT_ATTR_OPTIONS, true)
+ if err != nil {
+ return
+ }
+ if opts == nil {
+ opts = make(Attrs)
+ }
+
+ switch typ {
+ case OVS_VPORT_TYPE_NETDEV:
+ s = NewNetdevVportSpec(name)
+ break
+
+ case OVS_VPORT_TYPE_INTERNAL:
+ s = NewInternalVportSpec(name)
+ break
+
+ case OVS_VPORT_TYPE_VXLAN:
+ s, err = parseVxlanVportSpec(name, opts)
+ break
+
+ default:
+ err = fmt.Errorf("unsupported vport type %d", typ)
+ }
+
+ return
+}
+
+func (dp DatapathHandle) CreateVport(spec VportSpec) (VportID, error) {
+ dpif := dp.dpif
+
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[VPORT].id)
+ req.PutGenlMsghdr(OVS_VPORT_CMD_NEW, OVS_VPORT_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ req.PutStringAttr(OVS_VPORT_ATTR_NAME, spec.Name())
+ req.PutUint32Attr(OVS_VPORT_ATTR_TYPE, spec.typeId())
+ req.PutNestedAttrs(OVS_VPORT_ATTR_OPTIONS, func() {
+ spec.optionNlAttrs(req)
+ })
+ req.PutUint32Attr(OVS_VPORT_ATTR_UPCALL_PID, 0)
+
+ resp, err := dpif.sock.Request(req)
+ if err != nil {
+ return 0, err
+ }
+
+ _, _, err = dpif.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
+ if err != nil {
+ return 0, err
+ }
+
+ id, _, err := parseVport(resp)
+ if err != nil {
+ return 0, err
+ }
+
+ return id, nil
+}
+
+func IsNoSuchVportError(err error) bool {
+ return err == NetlinkError(syscall.ENODEV)
+}
+
+type Vport struct {
+ ID VportID
+ Spec VportSpec
+}
+
+func lookupVport(dpif *Dpif, dpifindex DatapathID, name string) (DatapathID, Vport, error) {
+ req := NewNlMsgBuilder(RequestFlags, dpif.families[VPORT].id)
+ req.PutGenlMsghdr(OVS_VPORT_CMD_GET, OVS_VPORT_VERSION)
+ req.putOvsHeader(dpifindex)
+ req.PutStringAttr(OVS_VPORT_ATTR_NAME, name)
+
+ resp, err := dpif.sock.Request(req)
+ if err != nil {
+ return 0, Vport{}, err
+ }
+
+ _, ovshdr, err := dpif.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
+ if err != nil {
+ return 0, Vport{}, err
+ }
+
+ id, s, err := parseVport(resp)
+ if err != nil {
+ return 0, Vport{}, err
+ }
+
+ return ovshdr.datapathID(), Vport{id, s}, nil
+}
+
+func (dpif *Dpif) LookupVportByName(name string) (DatapathHandle, Vport, error) {
+ dpifindex, vport, err := lookupVport(dpif, 0, name)
+ return DatapathHandle{dpif: dpif, ifindex: dpifindex}, vport, err
+}
+
+func (dp DatapathHandle) LookupVportByName(name string) (Vport, error) {
+ _, vport, err := lookupVport(dp.dpif, dp.ifindex, name)
+ return vport, err
+}
+
+func (dp DatapathHandle) LookupVport(id VportID) (Vport, error) {
+ req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[VPORT].id)
+ req.PutGenlMsghdr(OVS_VPORT_CMD_GET, OVS_VPORT_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ req.PutUint32Attr(OVS_VPORT_ATTR_PORT_NO, uint32(id))
+
+ resp, err := dp.dpif.sock.Request(req)
+ if err != nil {
+ return Vport{}, err
+ }
+
+ err = dp.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
+ if err != nil {
+ return Vport{}, err
+ }
+
+ id, s, err := parseVport(resp)
+ if err != nil {
+ return Vport{}, err
+ }
+
+ return Vport{id, s}, nil
+}
+
+func (dp DatapathHandle) LookupVportName(id VportID) (string, error) {
+ vport, err := dp.LookupVport(id)
+ if err != nil {
+ if !IsNoSuchVportError(err) {
+ return "", err
+ }
+
+ // No vport with the given port number, so just
+ // show the number
+ return fmt.Sprintf("%d:%d", dp.ifindex, id), nil
+ }
+
+ return vport.Spec.Name(), nil
+}
+
+func (dp DatapathHandle) EnumerateVports() ([]Vport, error) {
+ req := NewNlMsgBuilder(DumpFlags, dp.dpif.families[VPORT].id)
+ req.PutGenlMsghdr(OVS_VPORT_CMD_GET, OVS_VPORT_VERSION)
+ req.putOvsHeader(dp.ifindex)
+
+ var res []Vport
+ consumer := func(resp *NlMsgParser) error {
+ err := dp.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
+ if err != nil {
+ return err
+ }
+
+ id, spec, err := parseVport(resp)
+ if err != nil {
+ return err
+ }
+
+ res = append(res, Vport{id, spec})
+ return nil
+ }
+
+ err := dp.dpif.sock.RequestMulti(req, consumer)
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+func (dp DatapathHandle) DeleteVport(id VportID) error {
+ req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[VPORT].id)
+ req.PutGenlMsghdr(OVS_VPORT_CMD_DEL, OVS_VPORT_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ req.PutUint32Attr(OVS_VPORT_ATTR_PORT_NO, uint32(id))
+
+ _, err := dp.dpif.sock.Request(req)
+ return err
+}
+
+func (dp DatapathHandle) setVportUpcallPortId(id VportID, pid uint32) error {
+ req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[VPORT].id)
+ req.PutGenlMsghdr(OVS_VPORT_CMD_SET, OVS_VPORT_VERSION)
+ req.putOvsHeader(dp.ifindex)
+ req.PutUint32Attr(OVS_VPORT_ATTR_PORT_NO, uint32(id))
+ req.PutUint32Attr(OVS_VPORT_ATTR_UPCALL_PID, pid)
+
+ _, err := dp.dpif.sock.Request(req)
+ return err
+}
+
+type VportEventsConsumer interface {
+ VportCreated(dpid DatapathID, vport Vport) error
+ VportDeleted(dpid DatapathID, vport Vport) error
+ Error(err error, stopped bool)
+}
+
+func (dpif *Dpif) ConsumeVportEvents(consumer VportEventsConsumer) (Cancelable, error) {
+ return DatapathHandle{dpif, -1}.ConsumeVportEvents(consumer)
+}
+
+func (dp DatapathHandle) ConsumeVportEvents(consumer VportEventsConsumer) (Cancelable, error) {
+ mcGroup, err := dp.dpif.getMCGroup(VPORT, "ovs_vport")
+ if err != nil {
+ return nil, err
+ }
+
+ consumeDpif, err := dp.dpif.Reopen()
+ if err != nil {
+ return nil, err
+ }
+
+ err = syscall.SetsockoptInt(consumeDpif.sock.fd, SOL_NETLINK, syscall.NETLINK_ADD_MEMBERSHIP, int(mcGroup))
+ if err != nil {
+ consumeDpif.Close()
+ return nil, err
+ }
+
+ go consumeDpif.consumeVportEvents(consumer, dp.ifindex)
+ return cancelableDpif{consumeDpif}, nil
+}
+
+func (dpif *Dpif) consumeVportEvents(consumer VportEventsConsumer, ifindex DatapathID) {
+ dpif.sock.consume(consumer, func(msg *NlMsgParser) error {
+ genlhdr, ovshdr, err := dpif.checkNlMsgHeaders(msg, VPORT, -1)
+ if err != nil {
+ return err
+ }
+
+ // filter by ifindex, if consuming on a specific datapath
+ if ifindex >= 0 && ovshdr.datapathID() != ifindex {
+ return nil
+ }
+
+ id, spec, err := parseVport(msg)
+ if err != nil {
+ return err
+ }
+
+ switch genlhdr.Cmd {
+ case OVS_VPORT_CMD_NEW:
+ return consumer.VportCreated(ovshdr.datapathID(), Vport{id, spec})
+
+ case OVS_VPORT_CMD_DEL:
+ return consumer.VportDeleted(ovshdr.datapathID(), Vport{id, spec})
+
+ default:
+ return nil
+ }
+ })
+}
diff --git a/vendor/github.com/weaveworks/weave/common/docker/client.go b/vendor/github.com/weaveworks/weave/common/docker/client.go
new file mode 100644
index 000000000..bd4b78f63
--- /dev/null
+++ b/vendor/github.com/weaveworks/weave/common/docker/client.go
@@ -0,0 +1,109 @@
+package docker
+
+import (
+ "errors"
+ "github.com/fsouza/go-dockerclient"
+
+ . "github.com/weaveworks/weave/common"
+)
+
+// An observer for container events
+type ContainerObserver interface {
+ ContainerStarted(ident string)
+ ContainerDied(ident string)
+}
+
+type Client struct {
+ *docker.Client
+}
+
+// NewClient creates a new Docker client and checks we can talk to Docker
+func NewClient(apiPath string) (*Client, error) {
+ dc, err := docker.NewClient(apiPath)
+ if err != nil {
+ return nil, err
+ }
+ client := &Client{dc}
+
+ return client, client.checkWorking(apiPath)
+}
+
+func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) {
+ dc, err := docker.NewVersionedClient(apiPath, apiVersionString)
+ if err != nil {
+ return nil, err
+ }
+ client := &Client{dc}
+
+ return client, client.checkWorking(apiPath)
+}
+
+func (c *Client) checkWorking(apiPath string) error {
+ env, err := c.Version()
+ if err != nil {
+ return err
+ }
+ Log.Infof("[docker] Using Docker API on %s: %v", apiPath, env)
+ return nil
+}
+
+// AddObserver adds an observer for docker events
+func (c *Client) AddObserver(ob ContainerObserver) error {
+ events := make(chan *docker.APIEvents)
+ if err := c.AddEventListener(events); err != nil {
+ Log.Errorf("[docker] Unable to add listener to Docker API: %s", err)
+ return err
+ }
+
+ go func() {
+ for event := range events {
+ switch event.Status {
+ case "start":
+ id := event.ID
+ ob.ContainerStarted(id)
+ case "die":
+ id := event.ID
+ ob.ContainerDied(id)
+ }
+ }
+ }()
+ return nil
+}
+
+// IsContainerNotRunning returns true if we have checked with Docker that the ID is not running
+func (c *Client) IsContainerNotRunning(idStr string) bool {
+ container, err := c.InspectContainer(idStr)
+ if err == nil {
+ return !container.State.Running
+ }
+ if _, notThere := err.(*docker.NoSuchContainer); notThere {
+ return true
+ }
+ Log.Errorf("[docker] Could not check container status: %s", err)
+ return false
+}
+
+// This is intended to find an IP address that we can reach the container on;
+// if it is on the Docker bridge network then that address; if on the host network
+// then localhost
+func (c *Client) GetContainerIP(nameOrID string) (string, error) {
+ Log.Debugf("Getting IP for container %s", nameOrID)
+ info, err := c.InspectContainer(nameOrID)
+ if err != nil {
+ return "", err
+ }
+ if info.NetworkSettings.Networks != nil {
+ Log.Debugln("Networks: ", info.NetworkSettings.Networks)
+ if bridgeNetwork, ok := info.NetworkSettings.Networks["bridge"]; ok {
+ return bridgeNetwork.IPAddress, nil
+ } else if _, ok := info.NetworkSettings.Networks["host"]; ok {
+ return "127.0.0.1", nil
+ }
+ } else if info.HostConfig.NetworkMode == "host" {
+ return "127.0.0.1", nil
+ }
+ if info.NetworkSettings.IPAddress == "" {
+ return "", errors.New("No IP address found for container " + nameOrID)
+ }
+ return info.NetworkSettings.IPAddress, nil
+}
diff --git a/vendor/github.com/weaveworks/weave/common/logging.go b/vendor/github.com/weaveworks/weave/common/logging.go
new file mode 100644
index 000000000..d42ad44a2
--- /dev/null
+++ b/vendor/github.com/weaveworks/weave/common/logging.go
@@ -0,0 +1,66 @@
+package common
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+
+ "github.com/Sirupsen/logrus"
+)
+
+type textFormatter struct {
+}
+
+// Based off logrus.TextFormatter, which behaves completely
+// differently when you don't want colored output
+func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ b := &bytes.Buffer{}
+
+ levelText := strings.ToUpper(entry.Level.String())[0:4]
+ timeStamp := entry.Time.Format("2006/01/02 15:04:05.000000")
+ if len(entry.Data) > 0 {
+ fmt.Fprintf(b, "%s: %s %-44s ", levelText, timeStamp, entry.Message)
+ for k, v := range entry.Data {
+ fmt.Fprintf(b, " %s=%v", k, v)
+ }
+ } else {
+ // No padding when there's no fields
+ fmt.Fprintf(b, "%s: %s %s", levelText, timeStamp, entry.Message)
+ }
+
+ b.WriteByte('\n')
+ return b.Bytes(), nil
+}
+
+var (
+ standardTextFormatter = &textFormatter{}
+)
+
+var (
+ Log *logrus.Logger
+)
+
+func init() {
+ Log = logrus.New()
+ Log.Formatter = standardTextFormatter
+}
+
+func SetLogLevel(levelname string) {
+ level, err := logrus.ParseLevel(levelname)
+ if err != nil {
+ Log.Fatal(err)
+ }
+ Log.Level = level
+}
+
+func CheckFatal(e error) {
+ if e != nil {
+ Log.Fatal(e)
+ }
+}
+
+func CheckWarn(e error) {
+ if e != nil {
+ Log.Warnln(e)
+ }
+}
diff --git a/vendor/github.com/weaveworks/weave/common/mflagext/listvar.go b/vendor/github.com/weaveworks/weave/common/mflagext/listvar.go
new file mode 100644
index 000000000..a23dc6a6b
--- /dev/null
+++ b/vendor/github.com/weaveworks/weave/common/mflagext/listvar.go
@@ -0,0 +1,31 @@
+package mflagext
+
+import (
+ "fmt"
+
+ "github.com/docker/docker/pkg/mflag"
+)
+
+type listOpts struct {
+ value *[]string
+ hasBeenSet bool
+}
+
+func ListVar(p *[]string, names []string, value []string, usage string) {
+ *p = value
+ mflag.Var(&listOpts{p, false}, names, usage)
+}
+
+func (opts *listOpts) Set(value string) error {
+ if opts.hasBeenSet {
+ (*opts.value) = append((*opts.value), value)
+ } else {
+ (*opts.value) = []string{value}
+ opts.hasBeenSet = true
+ }
+ return nil
+}
+
+func (opts *listOpts) String() string {
+ return fmt.Sprintf("%v", []string(*opts.value))
+}
diff --git a/vendor/github.com/weaveworks/weave/common/odp/odp.go b/vendor/github.com/weaveworks/weave/common/odp/odp.go
new file mode 100644
index 000000000..53b481ff1
--- /dev/null
+++ b/vendor/github.com/weaveworks/weave/common/odp/odp.go
@@ -0,0 +1,93 @@
+package odp
+
+import (
+ "fmt"
+ "net"
+ "syscall"
+
+ "github.com/weaveworks/go-odp/odp"
+)
+
+// ODP admin functionality
+
+func CreateDatapath(dpname string) (err error, supported bool) {
+ dpif, err := odp.NewDpif()
+ if err != nil {
+ if odp.IsKernelLacksODPError(err) {
+ return nil, false
+ }
+
+ return err, true
+ }
+
+ defer dpif.Close()
+
+ dp, err := dpif.CreateDatapath(dpname)
+ if err != nil && !odp.IsDatapathNameAlreadyExistsError(err) {
+ return err, true
+ }
+
+ // Pick an ephemeral port number to use in probing for vxlan
+ // support.
+ udpconn, err := net.ListenUDP("udp4", nil)
+ if err != nil {
+ return err, true
+ }
+
+ // we leave the UDP socket open, so creating a vxlan vport on
+ // the same port number should fail. But that's fine: It's
+ // still sufficient to probe for support.
+ portno := uint16(udpconn.LocalAddr().(*net.UDPAddr).Port)
+ vpid, err := dp.CreateVport(odp.NewVxlanVportSpec(
+ fmt.Sprintf("vxlan-%d", portno), portno))
+ if nlerr, ok := err.(odp.NetlinkError); ok {
+ if syscall.Errno(nlerr) == syscall.EAFNOSUPPORT {
+ dp.Delete()
+ return fmt.Errorf("kernel does not have Open vSwitch VXLAN support"), false
+ }
+ }
+
+ if err == nil {
+ dp.DeleteVport(vpid)
+ }
+
+ udpconn.Close()
+ return nil, true
+}
+
+func DeleteDatapath(dpname string) error {
+ dpif, err := odp.NewDpif()
+ if err != nil {
+ return err
+ }
+
+ defer dpif.Close()
+
+ dp, err := dpif.LookupDatapath(dpname)
+ if err != nil {
+ if odp.IsNoSuchDatapathError(err) {
+ return nil
+ }
+
+ return err
+ }
+
+ return dp.Delete()
+}
+
+func AddDatapathInterface(dpname string, ifname string) error {
+ dpif, err := odp.NewDpif()
+ if err != nil {
+ return err
+ }
+
+ defer dpif.Close()
+
+ dp, err := dpif.LookupDatapath(dpname)
+ if err != nil {
+ return err
+ }
+
+ _, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
+ return err
+}
diff --git a/vendor/github.com/weaveworks/weave/common/signals.go b/vendor/github.com/weaveworks/weave/common/signals.go
new file mode 100644
index 000000000..1b99ec8e3
--- /dev/null
+++ b/vendor/github.com/weaveworks/weave/common/signals.go
@@ -0,0 +1,32 @@
+package common
+
+import (
+ "os"
+ "os/signal"
+ "runtime"
+ "syscall"
+)
+
+// A subsystem/server/... that can be stopped or queried about the status with a signal
+type SignalReceiver interface {
+ Stop() error
+}
+
+func SignalHandlerLoop(ss ...SignalReceiver) {
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
+ buf := make([]byte, 1<<20)
+ for {
+ switch <-sigs {
+ case syscall.SIGINT, syscall.SIGTERM:
+ Log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting")
+ for _, subsystem := range ss {
+ subsystem.Stop()
+ }
+ return
+ case syscall.SIGQUIT:
+ stacklen := runtime.Stack(buf, true)
+ Log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen])
+ }
+ }
+}
diff --git a/vendor/github.com/weaveworks/weave/common/utils.go b/vendor/github.com/weaveworks/weave/common/utils.go
new file mode 100644
index 000000000..829593219
--- /dev/null
+++ b/vendor/github.com/weaveworks/weave/common/utils.go
@@ -0,0 +1,20 @@
+package common
+
+import (
+ "strings"
+)
+
+// Assert test is true, panic otherwise
+func Assert(test bool) {
+ if !test {
+ panic("Assertion failure")
+ }
+}
+
+func ErrorMessages(errors []error) string {
+ var result []string
+ for _, err := range errors {
+ result = append(result, err.Error())
+ }
+ return strings.Join(result, "\n")
+}
diff --git a/vendor/manifest b/vendor/manifest
index 229349ce4..562643fd9 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -27,6 +27,12 @@
"branch": "master",
"path": "/handlers"
},
+ {
+ "importpath": "github.com/Sirupsen/logrus",
+ "repository": "https://github.com/Sirupsen/logrus",
+ "revision": "cdaedc68f2894175ac2b3221869685602c759e71",
+ "branch": "master"
+ },
{
"importpath": "github.com/armon/go-metrics",
"repository": "https://github.com/armon/go-metrics",
@@ -430,6 +436,20 @@
"branch": "master",
"path": "/spew"
},
+ {
+ "importpath": "github.com/docker/docker/pkg/homedir",
+ "repository": "https://github.com/docker/docker",
+ "revision": "7c1c96551d41e369a588e365a9bb99acb5bc8fdb",
+ "branch": "master",
+ "path": "/pkg/homedir"
+ },
+ {
+ "importpath": "github.com/docker/docker/pkg/mflag",
+ "repository": "https://github.com/docker/docker",
+ "revision": "7c1c96551d41e369a588e365a9bb99acb5bc8fdb",
+ "branch": "master",
+ "path": "/pkg/mflag"
+ },
{
"importpath": "github.com/docker/docker/pkg/mount",
"repository": "https://github.com/docker/docker",
@@ -604,6 +624,13 @@
"revision": "179d4d0c4d8d407a32af483c2354df1d2c91e6c3",
"branch": "master"
},
+ {
+ "importpath": "github.com/opencontainers/runc/libcontainer/user",
+ "repository": "https://github.com/opencontainers/runc",
+ "revision": "3317785f562b363eb386a2fa4909a55f267088c8",
+ "branch": "master",
+ "path": "/libcontainer/user"
+ },
{
"importpath": "github.com/pborman/uuid",
"repository": "https://github.com/pborman/uuid",
@@ -682,12 +709,26 @@
"branch": "master",
"path": "/codec"
},
+ {
+ "importpath": "github.com/weaveworks/go-odp/odp",
+ "repository": "https://github.com/weaveworks/go-odp",
+ "revision": "f8c8c40c18898d7c4f6be33978d68f5d2810f373",
+ "branch": "master",
+ "path": "/odp"
+ },
{
"importpath": "github.com/weaveworks/procspy",
"repository": "https://github.com/weaveworks/procspy",
"revision": "cb970aa190c374d1e47711dbffb3c2c6e9ef0dd1",
"branch": "master"
},
+ {
+ "importpath": "github.com/weaveworks/weave/common",
+ "repository": "https://github.com/weaveworks/weave",
+ "revision": "29f3d711c65121f436a9d191af4633ba4600d0fd",
+ "branch": "master",
+ "path": "/common"
+ },
{
"importpath": "golang.org/x/crypto/curve25519",
"repository": "https://go.googlesource.com/crypto",