From a9b868d310ec2129085e54ee4d4f39ecf88700f8 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Fri, 4 Dec 2015 09:57:30 +0000 Subject: [PATCH] Vendor in github.com/weaveworks/weave/common --- .../github.com/Sirupsen/logrus/CHANGELOG.md | 55 + vendor/github.com/Sirupsen/logrus/LICENSE | 21 + vendor/github.com/Sirupsen/logrus/README.md | 365 +++++ vendor/github.com/Sirupsen/logrus/doc.go | 26 + vendor/github.com/Sirupsen/logrus/entry.go | 264 ++++ .../github.com/Sirupsen/logrus/entry_test.go | 77 + .../Sirupsen/logrus/examples/basic/basic.go | 50 + .../Sirupsen/logrus/examples/hook/hook.go | 30 + vendor/github.com/Sirupsen/logrus/exported.go | 193 +++ .../github.com/Sirupsen/logrus/formatter.go | 48 + .../Sirupsen/logrus/formatter_bench_test.go | 98 ++ .../logrus/formatters/logstash/logstash.go | 56 + .../formatters/logstash/logstash_test.go | 52 + .../github.com/Sirupsen/logrus/hook_test.go | 122 ++ vendor/github.com/Sirupsen/logrus/hooks.go | 34 + .../Sirupsen/logrus/hooks/syslog/README.md | 39 + .../Sirupsen/logrus/hooks/syslog/syslog.go | 61 + .../logrus/hooks/syslog/syslog_test.go | 26 + .../Sirupsen/logrus/json_formatter.go | 41 + .../Sirupsen/logrus/json_formatter_test.go | 120 ++ vendor/github.com/Sirupsen/logrus/logger.go | 212 +++ vendor/github.com/Sirupsen/logrus/logrus.go | 98 ++ .../github.com/Sirupsen/logrus/logrus_test.go | 301 ++++ .../Sirupsen/logrus/terminal_bsd.go | 9 + .../Sirupsen/logrus/terminal_linux.go | 12 + .../Sirupsen/logrus/terminal_notwindows.go | 21 + .../Sirupsen/logrus/terminal_solaris.go | 15 + .../Sirupsen/logrus/terminal_windows.go | 27 + .../Sirupsen/logrus/text_formatter.go | 161 ++ .../Sirupsen/logrus/text_formatter_test.go | 61 + vendor/github.com/Sirupsen/logrus/writer.go | 31 + .../docker/docker/pkg/homedir/homedir.go | 39 + .../docker/docker/pkg/homedir/homedir_test.go | 24 + .../docker/docker/pkg/mflag/LICENSE | 27 + .../docker/docker/pkg/mflag/README.md | 40 + .../docker/pkg/mflag/example/example.go | 36 + .../docker/docker/pkg/mflag/flag.go | 1264 +++++++++++++++ .../docker/docker/pkg/mflag/flag_test.go | 516 +++++++ .../runc/libcontainer/user/MAINTAINERS | 2 + .../runc/libcontainer/user/lookup.go | 108 ++ .../runc/libcontainer/user/lookup_unix.go | 30 + .../libcontainer/user/lookup_unsupported.go | 21 + .../runc/libcontainer/user/user.go | 418 +++++ .../runc/libcontainer/user/user_test.go | 472 ++++++ .../weaveworks/go-odp/odp/datapath.go | 172 +++ .../github.com/weaveworks/go-odp/odp/dpif.go | 181 +++ .../weaveworks/go-odp/odp/dpif_test.go | 437 ++++++ .../github.com/weaveworks/go-odp/odp/flow.go | 1349 +++++++++++++++++ .../weaveworks/go-odp/odp/genetlink.go | 101 ++ .../weaveworks/go-odp/odp/netlink.go | 698 +++++++++ .../weaveworks/go-odp/odp/packet.go | 173 +++ .../weaveworks/go-odp/odp/syscall.go | 220 +++ .../weaveworks/go-odp/odp/unsafe.go | 82 + .../github.com/weaveworks/go-odp/odp/vport.go | 362 +++++ .../weaveworks/weave/common/docker/client.go | 109 ++ .../weaveworks/weave/common/logging.go | 66 + .../weave/common/mflagext/listvar.go | 31 + .../weaveworks/weave/common/odp/odp.go | 93 ++ .../weaveworks/weave/common/signals.go | 32 + .../weaveworks/weave/common/utils.go | 20 + vendor/manifest | 41 + 61 files changed, 9890 insertions(+) create mode 100644 vendor/github.com/Sirupsen/logrus/CHANGELOG.md create mode 100644 vendor/github.com/Sirupsen/logrus/LICENSE create mode 100644 vendor/github.com/Sirupsen/logrus/README.md create mode 100644 vendor/github.com/Sirupsen/logrus/doc.go create mode 100644 vendor/github.com/Sirupsen/logrus/entry.go create mode 100644 vendor/github.com/Sirupsen/logrus/entry_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/examples/basic/basic.go create mode 100644 vendor/github.com/Sirupsen/logrus/examples/hook/hook.go create mode 100644 vendor/github.com/Sirupsen/logrus/exported.go create mode 100644 vendor/github.com/Sirupsen/logrus/formatter.go create mode 100644 vendor/github.com/Sirupsen/logrus/formatter_bench_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go create mode 100644 vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/hook_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/hooks.go create mode 100644 vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md create mode 100644 vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go create mode 100644 vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/json_formatter.go create mode 100644 vendor/github.com/Sirupsen/logrus/json_formatter_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/logger.go create mode 100644 vendor/github.com/Sirupsen/logrus/logrus.go create mode 100644 vendor/github.com/Sirupsen/logrus/logrus_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_bsd.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_linux.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_notwindows.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_solaris.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_windows.go create mode 100644 vendor/github.com/Sirupsen/logrus/text_formatter.go create mode 100644 vendor/github.com/Sirupsen/logrus/text_formatter_test.go create mode 100644 vendor/github.com/Sirupsen/logrus/writer.go create mode 100644 vendor/github.com/docker/docker/pkg/homedir/homedir.go create mode 100644 vendor/github.com/docker/docker/pkg/homedir/homedir_test.go create mode 100644 vendor/github.com/docker/docker/pkg/mflag/LICENSE create mode 100644 vendor/github.com/docker/docker/pkg/mflag/README.md create mode 100644 vendor/github.com/docker/docker/pkg/mflag/example/example.go create mode 100644 vendor/github.com/docker/docker/pkg/mflag/flag.go create mode 100644 vendor/github.com/docker/docker/pkg/mflag/flag_test.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/MAINTAINERS create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/user.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/user_test.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/datapath.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/dpif.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/dpif_test.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/flow.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/genetlink.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/netlink.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/packet.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/syscall.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/unsafe.go create mode 100644 vendor/github.com/weaveworks/go-odp/odp/vport.go create mode 100644 vendor/github.com/weaveworks/weave/common/docker/client.go create mode 100644 vendor/github.com/weaveworks/weave/common/logging.go create mode 100644 vendor/github.com/weaveworks/weave/common/mflagext/listvar.go create mode 100644 vendor/github.com/weaveworks/weave/common/odp/odp.go create mode 100644 vendor/github.com/weaveworks/weave/common/signals.go create mode 100644 vendor/github.com/weaveworks/weave/common/utils.go 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 :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][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): + +![Colored](http://i.imgur.com/PY7qMwd.png) + +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 :walrus: + +## 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",