From 142ffadc2cdcc96746de241c501fe63689ee669d Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Mon, 6 Apr 2015 18:55:29 -0700 Subject: [PATCH] Refactor logging to focus on simple STDOUT/STDERR This also lays the foundation for the possibility of log drivers in the future, if it is decided that is a direction to pursue. Signed-off-by: Nathan LeClaire --- Godeps/Godeps.json | 15 - .../src/github.com/Sirupsen/logrus/.gitignore | 1 - .../github.com/Sirupsen/logrus/.travis.yml | 9 - .../src/github.com/Sirupsen/logrus/LICENSE | 21 - .../src/github.com/Sirupsen/logrus/README.md | 349 --- .../src/github.com/Sirupsen/logrus/entry.go | 248 -- .../github.com/Sirupsen/logrus/entry_test.go | 53 - .../Sirupsen/logrus/examples/basic/basic.go | 40 - .../Sirupsen/logrus/examples/hook/hook.go | 35 - .../github.com/Sirupsen/logrus/exported.go | 182 -- .../github.com/Sirupsen/logrus/formatter.go | 44 - .../Sirupsen/logrus/formatter_bench_test.go | 88 - .../github.com/Sirupsen/logrus/hook_test.go | 122 - .../src/github.com/Sirupsen/logrus/hooks.go | 34 - .../logrus/hooks/airbrake/airbrake.go | 54 - .../logrus/hooks/papertrail/README.md | 28 - .../logrus/hooks/papertrail/papertrail.go | 54 - .../hooks/papertrail/papertrail_test.go | 26 - .../Sirupsen/logrus/hooks/syslog/README.md | 20 - .../Sirupsen/logrus/hooks/syslog/syslog.go | 59 - .../logrus/hooks/syslog/syslog_test.go | 26 - .../Sirupsen/logrus/json_formatter.go | 22 - .../src/github.com/Sirupsen/logrus/logger.go | 161 - .../src/github.com/Sirupsen/logrus/logrus.go | 94 - .../github.com/Sirupsen/logrus/logrus_test.go | 247 -- .../Sirupsen/logrus/terminal_darwin.go | 12 - .../Sirupsen/logrus/terminal_freebsd.go | 20 - .../Sirupsen/logrus/terminal_linux.go | 12 - .../Sirupsen/logrus/terminal_notwindows.go | 21 - .../Sirupsen/logrus/terminal_windows.go | 27 - .../Sirupsen/logrus/text_formatter.go | 95 - .../github.com/docker/docker/api/MAINTAINERS | 2 - .../github.com/docker/docker/api/README.md | 5 - .../docker/docker/api/api_unit_test.go | 19 - .../docker/docker/api/client/cli.go | 188 -- .../docker/docker/api/client/commands.go | 2764 ----------------- .../docker/docker/api/client/hijack.go | 250 -- .../docker/docker/api/client/utils.go | 296 -- .../github.com/docker/docker/api/common.go | 75 - .../docker/docker/api/server/MAINTAINERS | 2 - .../docker/docker/api/server/server.go | 1653 ---------- .../docker/api/server/server_unit_test.go | 553 ---- .../docker/docker/api/stats/stats.go | 87 - commands/active.go | 3 +- commands/commands.go | 2 +- commands/config.go | 3 +- commands/create.go | 2 +- commands/env.go | 2 +- commands/inspect.go | 2 +- commands/ip.go | 3 +- commands/kill.go | 2 +- commands/ls.go | 2 +- commands/regeneratecerts.go | 2 +- commands/restart.go | 2 +- commands/rm.go | 2 +- commands/ssh.go | 2 +- commands/start.go | 2 +- commands/stop.go | 2 +- commands/upgrade.go | 2 +- commands/url.go | 2 +- drivers/amazonec2/amazonec2.go | 2 +- drivers/azure/azure.go | 4 +- drivers/digitalocean/digitalocean.go | 2 +- drivers/drivers.go | 2 +- drivers/google/auth_util.go | 2 +- drivers/google/compute_util.go | 2 +- drivers/google/google.go | 2 +- drivers/hyperv/hyperv_windows.go | 2 +- drivers/hyperv/powershell_windows.go | 2 +- drivers/none/none.go | 7 +- drivers/openstack/client.go | 2 +- drivers/openstack/openstack.go | 4 +- drivers/rackspace/client.go | 2 +- drivers/rackspace/rackspace.go | 2 +- drivers/softlayer/driver.go | 2 +- drivers/virtualbox/vbm.go | 2 +- drivers/virtualbox/virtualbox.go | 2 +- drivers/vmwarefusion/fusion_darwin.go | 2 +- drivers/vmwarefusion/vmrun_darwin.go | 2 +- drivers/vmwarevcloudair/vcloudair.go | 4 +- drivers/vmwarevsphere/govc.go | 2 +- drivers/vmwarevsphere/vc_conn.go | 2 +- drivers/vmwarevsphere/vsphere.go | 2 +- libmachine/filestore.go | 2 +- libmachine/host.go | 2 +- libmachine/provision/boot2docker.go | 2 +- libmachine/provision/os_release.go | 2 +- libmachine/provision/ubuntu.go | 2 +- libmachine/provision/utils.go | 2 +- log.go | 12 - log/log.go | 123 + log/log_test.go | 19 + log/terminal.go | 129 + main.go | 3 +- ssh/ssh.go | 4 +- utils/b2d.go | 2 +- utils/utils.go | 34 +- 97 files changed, 356 insertions(+), 8189 deletions(-) delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go delete mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/MAINTAINERS delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/README.md delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/api_unit_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/client/cli.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/client/commands.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/client/hijack.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/client/utils.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/common.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/server/MAINTAINERS delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/server/server.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/server/server_unit_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/api/stats/stats.go delete mode 100644 log.go create mode 100644 log/log.go create mode 100644 log/log_test.go create mode 100644 log/terminal.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 6bd860c7f0..8f66b8e9ae 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -12,11 +12,6 @@ "Comment": "v1.1-17-g515f3ec", "Rev": "515f3ec74ce6a5b31e934cefae997c97bd0a1b1e" }, - { - "ImportPath": "github.com/Sirupsen/logrus", - "Comment": "v0.6.1", - "Rev": "1f2ba2c6317323dd667bd266c1e8ebffc4a4c62f" - }, { "ImportPath": "github.com/cenkalti/backoff", "Rev": "9831e1e25c874e0a0601b6dc43641071414eec7a" @@ -31,11 +26,6 @@ "Comment": "v0.5.0", "Rev": "5478aae80694de1d2d0e02c386bbedd201266234" }, - { - "ImportPath": "github.com/docker/docker/api", - "Comment": "v1.5.0", - "Rev": "a8a31eff10544860d2188dddabdee4d727545796" - }, { "ImportPath": "github.com/docker/docker/dockerversion", "Comment": "v1.5.0", @@ -106,11 +96,6 @@ "Comment": "v1.5.0", "Rev": "a8a31eff10544860d2188dddabdee4d727545796" }, - { - "ImportPath": "github.com/docker/docker/utils", - "Comment": "v1.5.0", - "Rev": "a8a31eff10544860d2188dddabdee4d727545796" - }, { "ImportPath": "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar", "Comment": "v1.5.0", diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore deleted file mode 100644 index 66be63a005..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore +++ /dev/null @@ -1 +0,0 @@ -logrus diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml deleted file mode 100644 index d5a559f840..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go -go: - - 1.2 - - 1.3 - - tip -install: - - go get github.com/stretchr/testify - - go get github.com/stvp/go-udp-testing - - go get github.com/tobi/airbrake-go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE b/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE deleted file mode 100644 index f090cb42f3..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md deleted file mode 100644 index cabd027ae9..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md +++ /dev/null @@ -1,349 +0,0 @@ -# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) - -Logrus is a structured logger for Go (golang), completely API compatible with -the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not -yet stable (pre 1.0), the core API is unlikely change much but please version -control your Logrus to make sure you aren't fetching latest `master` on every -build.** - -Nicely color-coded in development (when a TTY is attached, otherwise just -plain text): - -![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(logrus.TextFormatter)` when a TTY is not -attached, the output is compatible with the -[l2met](http://r.32k.io/l2met-introduction) format: - -```text -time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10 -time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122 -time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10 -time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9 -time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100 -``` - -#### Example - -The simplest way to use Logrus is simply the package-level exported logger: - -```go -package main - -import ( - log "github.com/Sirupsen/logrus" -) - -func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - }).Info("A walrus appears") -} -``` - -Note that it's completely api-compatible with the stdlib logger, so you can -replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` -and you'll now have the flexibility of Logrus. You can customize it all you -want: - -```go -package main - -import ( - "os" - log "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/airbrake" -) - -func init() { - // Log as JSON instead of the default ASCII formatter. - log.SetFormatter(&log.JSONFormatter{}) - - // Use the Airbrake hook to report errors that have Error severity or above to - // an exception tracker. You can create custom hooks, see the Hooks section. - log.AddHook(&logrus_airbrake.AirbrakeHook{}) - - // Output to stderr instead of stdout, could also be a file. - log.SetOutput(os.Stderr) - - // Only log the warning severity or above. - log.SetLevel(log.WarnLevel) -} - -func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(log.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(log.Fields{ - "omg": true, - "number": 100, - }).Fatal("The ice breaks!") -} -``` - -For more advanced usage such as logging to multiple locations from the same -application, you can also create an instance of the `logrus` Logger: - -```go -package main - -import ( - "github.com/Sirupsen/logrus" -) - -// Create a new instance of the logger. You can have any number of instances. -var log = logrus.New() - -func main() { - // The API for setting attributes is a little different than the package level - // exported logger. See Godoc. - log.Out = os.Stderr - - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") -} -``` - -#### Fields - -Logrus encourages careful, structured logging though logging fields instead of -long, unparseable error messages. For example, instead of: `log.Fatalf("Failed -to send event %s to topic %s with key %d")`, you should log the much more -discoverable: - -```go -log.WithFields(log.Fields{ - "event": event, - "topic": topic, - "key": key, -}).Fatal("Failed to send event") -``` - -We've found this API forces you to think about logging in a way that produces -much more useful logging messages. We've been in countless situations where just -a single added field to a log statement that was already there would've saved us -hours. The `WithFields` call is optional. - -In general, with Logrus using any of the `printf`-family functions should be -seen as a hint you should add a field, however, you can still use the -`printf`-family functions with Logrus. - -#### Hooks - -You can add hooks for logging levels. For example to send errors to an exception -tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to -multiple places simultaneously, e.g. syslog. - -```go -// Not the real implementation of the Airbrake hook. Just a simple sample. -import ( - log "github.com/Sirupsen/logrus" -) - -func init() { - log.AddHook(new(AirbrakeHook)) -} - -type AirbrakeHook struct{} - -// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains -// the fields for the entry. See the Fields section of the README. -func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error { - err := airbrake.Notify(entry.Data["error"].(error)) - if err != nil { - log.WithFields(log.Fields{ - "source": "airbrake", - "endpoint": airbrake.Endpoint, - }).Info("Failed to send error to Airbrake") - } - - return nil -} - -// `Levels()` returns a slice of `Levels` the hook is fired for. -func (hook *AirbrakeHook) Levels() []log.Level { - return []log.Level{ - log.ErrorLevel, - log.FatalLevel, - log.PanicLevel, - } -} -``` - -Logrus comes with built-in hooks. Add those, or your custom hook, in `init`: - -```go -import ( - log "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/airbrake" - "github.com/Sirupsen/logrus/hooks/syslog" - "log/syslog" -) - -func init() { - log.AddHook(new(logrus_airbrake.AirbrakeHook)) - - hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") - if err != nil { - log.Error("Unable to connect to local syslog daemon") - } else { - log.AddHook(hook) - } -} -``` - -* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) - Send errors to an exception tracking service compatible with the Airbrake API. - Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. - -* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) - Send errors to the Papertrail hosted logging service via UDP. - -* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) - Send errors to remote syslog server. - Uses standard library `log/syslog` behind the scenes. - -* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus) - Send errors to a channel in hipchat. - -#### Level logging - -Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic. - -```go -log.Debug("Useful debugging information.") -log.Info("Something noteworthy happened!") -log.Warn("You should probably take a look at this.") -log.Error("Something failed but I'm not quitting.") -// Calls os.Exit(1) after logging -log.Fatal("Bye.") -// Calls panic() after logging -log.Panic("I'm bailing.") -``` - -You can set the logging level on a `Logger`, then it will only log entries with -that severity or anything above it: - -```go -// Will log anything that is info or above (warn, error, fatal, panic). Default. -log.SetLevel(log.InfoLevel) -``` - -It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose -environment if your application has that. - -#### Entries - -Besides the fields added with `WithField` or `WithFields` some fields are -automatically added to all logging events: - -1. `time`. The timestamp when the entry was created. -2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after - the `AddFields` call. E.g. `Failed to send event.` -3. `level`. The logging level. E.g. `info`. - -#### Environments - -Logrus has no notion of environment. - -If you wish for hooks and formatters to only be used in specific environments, -you should handle that yourself. For example, if your application has a global -variable `Environment`, which is a string representation of the environment you -could do: - -```go -import ( - log "github.com/Sirupsen/logrus" -) - -init() { - // do something here to set environment depending on an environment variable - // or command-line flag - if Environment == "production" { - log.SetFormatter(logrus.JSONFormatter) - } else { - // The TextFormatter is default, you don't actually have to do this. - log.SetFormatter(logrus.TextFormatter) - } -} -``` - -This configuration is how `logrus` was intended to be used, but JSON in -production is mostly only useful if you do log aggregation with tools like -Splunk or Logstash. - -#### Formatters - -The built-in logging formatters are: - -* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise - without colors. - * *Note:* to force colored output when there is no TTY, set the `ForceColors` - field to `true`. To force no colored output even if there is a TTY set the - `DisableColors` field to `true` -* `logrus.JSONFormatter`. Logs fields as JSON. - -Third party logging formatters: - -* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. - -You can define your formatter by implementing the `Formatter` interface, -requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a -`Fields` type (`map[string]interface{}`) with all your fields as well as the -default ones (see Entries section above): - -```go -type MyJSONFormatter struct { -} - -log.SetFormatter(new(MyJSONFormatter)) - -func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - // Note this doesn't include Time, Level and Message which are available on - // the Entry. Consult `godoc` on information about those fields or read the - // source of the official loggers. - serialized, err := json.Marshal(entry.Data) - if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) - } - return append(serialized, '\n'), nil -} -``` - -#### Rotation - -Log rotation is not provided with Logrus. Log rotation should be done by an -external program (like `logrotated(8)`) that can compress and delete old log -entries. It should not be a feature of the application-level logger. - - -[godoc]: https://godoc.org/github.com/Sirupsen/logrus diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go deleted file mode 100644 index e164eecb5f..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go +++ /dev/null @@ -1,248 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "io" - "os" - "time" -) - -// An entry is the final or intermediate Logrus logging entry. It contains all -// the fields passed with WithField{,s}. It's finally logged when Debug, Info, -// Warn, Error, Fatal or Panic is called on it. These objects can be reused and -// passed around as much as you wish to avoid field duplication. -type Entry struct { - Logger *Logger - - // Contains all the fields set by the user. - Data Fields - - // Time at which the log entry was created - Time time.Time - - // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic - Level Level - - // Message passed to Debug, Info, Warn, Error, Fatal or Panic - Message string -} - -func NewEntry(logger *Logger) *Entry { - return &Entry{ - Logger: logger, - // Default is three fields, give a little extra room - Data: make(Fields, 5), - } -} - -// Returns a reader for the entry, which is a proxy to the formatter. -func (entry *Entry) Reader() (*bytes.Buffer, error) { - serialized, err := entry.Logger.Formatter.Format(entry) - return bytes.NewBuffer(serialized), err -} - -// Returns the string representation from the reader and ultimately the -// formatter. -func (entry *Entry) String() (string, error) { - reader, err := entry.Reader() - if err != nil { - return "", err - } - - return reader.String(), err -} - -// Add a single field to the Entry. -func (entry *Entry) WithField(key string, value interface{}) *Entry { - return entry.WithFields(Fields{key: value}) -} - -// Add a map of fields to the Entry. -func (entry *Entry) WithFields(fields Fields) *Entry { - data := Fields{} - for k, v := range entry.Data { - data[k] = v - } - for k, v := range fields { - data[k] = v - } - return &Entry{Logger: entry.Logger, Data: data} -} - -func (entry *Entry) log(level Level, msg string) { - entry.Time = time.Now() - entry.Level = level - entry.Message = msg - - if err := entry.Logger.Hooks.Fire(level, entry); err != nil { - entry.Logger.mu.Lock() - fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) - entry.Logger.mu.Unlock() - } - - reader, err := entry.Reader() - if err != nil { - entry.Logger.mu.Lock() - fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) - entry.Logger.mu.Unlock() - } - - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - - _, err = io.Copy(entry.Logger.Out, reader) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) - } - - // To avoid Entry#log() returning a value that only would make sense for - // panic() to use in Entry#Panic(), we avoid the allocation by checking - // directly here. - if level <= PanicLevel { - panic(entry) - } -} - -func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { - entry.log(DebugLevel, fmt.Sprint(args...)) - } -} - -func (entry *Entry) Print(args ...interface{}) { - entry.Info(args...) -} - -func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { - entry.log(InfoLevel, fmt.Sprint(args...)) - } -} - -func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { - entry.log(WarnLevel, fmt.Sprint(args...)) - } -} - -func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { - entry.log(ErrorLevel, fmt.Sprint(args...)) - } -} - -func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { - entry.log(FatalLevel, fmt.Sprint(args...)) - } - os.Exit(1) -} - -func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { - entry.log(PanicLevel, fmt.Sprint(args...)) - } - panic(fmt.Sprint(args...)) -} - -// Entry Printf family functions - -func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Logger.Level >= DebugLevel { - entry.Debug(fmt.Sprintf(format, args...)) - } -} - -func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.Level >= InfoLevel { - entry.Info(fmt.Sprintf(format, args...)) - } -} - -func (entry *Entry) Printf(format string, args ...interface{}) { - entry.Infof(format, args...) -} - -func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.Level >= WarnLevel { - entry.Warn(fmt.Sprintf(format, args...)) - } -} - -func (entry *Entry) Warningf(format string, args ...interface{}) { - entry.Warnf(format, args...) -} - -func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { - entry.Error(fmt.Sprintf(format, args...)) - } -} - -func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.Level >= FatalLevel { - entry.Fatal(fmt.Sprintf(format, args...)) - } -} - -func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.Level >= PanicLevel { - entry.Panic(fmt.Sprintf(format, args...)) - } -} - -// Entry Println family functions - -func (entry *Entry) Debugln(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { - entry.Debug(entry.sprintlnn(args...)) - } -} - -func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { - entry.Info(entry.sprintlnn(args...)) - } -} - -func (entry *Entry) Println(args ...interface{}) { - entry.Infoln(args...) -} - -func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { - entry.Warn(entry.sprintlnn(args...)) - } -} - -func (entry *Entry) Warningln(args ...interface{}) { - entry.Warnln(args...) -} - -func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { - entry.Error(entry.sprintlnn(args...)) - } -} - -func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { - entry.Fatal(entry.sprintlnn(args...)) - } -} - -func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { - entry.Panic(entry.sprintlnn(args...)) - } -} - -// Sprintlnn => Sprint no newline. This is to get the behavior of how -// fmt.Sprintln where spaces are always added between operands, regardless of -// their type. Instead of vendoring the Sprintln implementation to spare a -// string allocation, we do the simplest thing. -func (entry *Entry) sprintlnn(args ...interface{}) string { - msg := fmt.Sprintln(args...) - return msg[:len(msg)-1] -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go deleted file mode 100644 index 98717df490..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -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/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go deleted file mode 100644 index a62ba45de5..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "github.com/Sirupsen/logrus" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.JSONFormatter) - log.Formatter = new(logrus.TextFormatter) // default -} - -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", - "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{ - "animal": "orca", - "size": 9009, - }).Panic("It's over 9000!") -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go deleted file mode 100644 index 42e7a4c982..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/airbrake" - "github.com/tobi/airbrake-go" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.TextFormatter) // default - log.Hooks.Add(new(logrus_airbrake.AirbrakeHook)) -} - -func main() { - airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml" - airbrake.ApiKey = "whatever" - airbrake.Environment = "production" - - 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/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go deleted file mode 100644 index d087124481..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go +++ /dev/null @@ -1,182 +0,0 @@ -package logrus - -import ( - "io" -) - -var ( - // std is the name of the standard logger in stdlib `log` - std = New() -) - -// SetOutput sets the standard logger output. -func SetOutput(out io.Writer) { - std.mu.Lock() - defer std.mu.Unlock() - std.Out = out -} - -// SetFormatter sets the standard logger formatter. -func SetFormatter(formatter Formatter) { - std.mu.Lock() - defer std.mu.Unlock() - std.Formatter = formatter -} - -// SetLevel sets the standard logger level. -func SetLevel(level Level) { - std.mu.Lock() - defer std.mu.Unlock() - std.Level = level -} - -// GetLevel returns the standard logger level. -func GetLevel() Level { - return std.Level -} - -// AddHook adds a hook to the standard logger hooks. -func AddHook(hook Hook) { - std.mu.Lock() - defer std.mu.Unlock() - std.Hooks.Add(hook) -} - -// WithField creates an entry from the standard logger and adds a field to -// it. If you want multiple fields, use `WithFields`. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithField(key string, value interface{}) *Entry { - return std.WithField(key, value) -} - -// WithFields creates an entry from the standard logger and adds multiple -// fields to it. This is simply a helper for `WithField`, invoking it -// once for each field. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithFields(fields Fields) *Entry { - return std.WithFields(fields) -} - -// Debug logs a message at level Debug on the standard logger. -func Debug(args ...interface{}) { - std.Debug(args...) -} - -// Print logs a message at level Info on the standard logger. -func Print(args ...interface{}) { - std.Print(args...) -} - -// Info logs a message at level Info on the standard logger. -func Info(args ...interface{}) { - std.Info(args...) -} - -// Warn logs a message at level Warn on the standard logger. -func Warn(args ...interface{}) { - std.Warn(args...) -} - -// Warning logs a message at level Warn on the standard logger. -func Warning(args ...interface{}) { - std.Warning(args...) -} - -// Error logs a message at level Error on the standard logger. -func Error(args ...interface{}) { - std.Error(args...) -} - -// Panic logs a message at level Panic on the standard logger. -func Panic(args ...interface{}) { - std.Panic(args...) -} - -// Fatal logs a message at level Fatal on the standard logger. -func Fatal(args ...interface{}) { - std.Fatal(args...) -} - -// Debugf logs a message at level Debug on the standard logger. -func Debugf(format string, args ...interface{}) { - std.Debugf(format, args...) -} - -// Printf logs a message at level Info on the standard logger. -func Printf(format string, args ...interface{}) { - std.Printf(format, args...) -} - -// Infof logs a message at level Info on the standard logger. -func Infof(format string, args ...interface{}) { - std.Infof(format, args...) -} - -// Warnf logs a message at level Warn on the standard logger. -func Warnf(format string, args ...interface{}) { - std.Warnf(format, args...) -} - -// Warningf logs a message at level Warn on the standard logger. -func Warningf(format string, args ...interface{}) { - std.Warningf(format, args...) -} - -// Errorf logs a message at level Error on the standard logger. -func Errorf(format string, args ...interface{}) { - std.Errorf(format, args...) -} - -// Panicf logs a message at level Panic on the standard logger. -func Panicf(format string, args ...interface{}) { - std.Panicf(format, args...) -} - -// Fatalf logs a message at level Fatal on the standard logger. -func Fatalf(format string, args ...interface{}) { - std.Fatalf(format, args...) -} - -// Debugln logs a message at level Debug on the standard logger. -func Debugln(args ...interface{}) { - std.Debugln(args...) -} - -// Println logs a message at level Info on the standard logger. -func Println(args ...interface{}) { - std.Println(args...) -} - -// Infoln logs a message at level Info on the standard logger. -func Infoln(args ...interface{}) { - std.Infoln(args...) -} - -// Warnln logs a message at level Warn on the standard logger. -func Warnln(args ...interface{}) { - std.Warnln(args...) -} - -// Warningln logs a message at level Warn on the standard logger. -func Warningln(args ...interface{}) { - std.Warningln(args...) -} - -// Errorln logs a message at level Error on the standard logger. -func Errorln(args ...interface{}) { - std.Errorln(args...) -} - -// Panicln logs a message at level Panic on the standard logger. -func Panicln(args ...interface{}) { - std.Panicln(args...) -} - -// Fatalln logs a message at level Fatal on the standard logger. -func Fatalln(args ...interface{}) { - std.Fatalln(args...) -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go deleted file mode 100644 index 74c49a0e0e..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go +++ /dev/null @@ -1,44 +0,0 @@ -package logrus - -// The Formatter interface is used to implement a custom Formatter. It takes an -// `Entry`. It exposes all the fields, including the default ones: -// -// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. -// * `entry.Data["time"]`. The timestamp. -// * `entry.Data["level"]. The level the entry was logged at. -// -// Any additional fields added with `WithField` or `WithFields` are also in -// `entry.Data`. Format is expected to return an array of bytes which are then -// logged to `logger.Out`. -type Formatter interface { - Format(*Entry) ([]byte, error) -} - -// This is to not silently overwrite `time`, `msg` and `level` fields when -// dumping it. If this code wasn't there doing: -// -// logrus.WithField("level", 1).Info("hello") -// -// Would just silently drop the user provided level. Instead with this code -// it'll logged as: -// -// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} -// -// It's not exported because it's still using Data in an opinionated way. It's to -// avoid code duplication between the two default formatters. -func prefixFieldClashes(entry *Entry) { - _, ok := entry.Data["time"] - if ok { - entry.Data["fields.time"] = entry.Data["time"] - } - - _, ok = entry.Data["msg"] - if ok { - entry.Data["fields.msg"] = entry.Data["msg"] - } - - _, ok = entry.Data["level"] - if ok { - entry.Data["fields.level"] = entry.Data["level"] - } -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go deleted file mode 100644 index 77989da629..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package logrus - -import ( - "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", -} - -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/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go deleted file mode 100644 index 13f34cb6f8..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go +++ /dev/null @@ -1,122 +0,0 @@ -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/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go deleted file mode 100644 index 0da2b3653f..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go +++ /dev/null @@ -1,34 +0,0 @@ -package logrus - -// A hook to be fired when logging on the logging levels returned from -// `Levels()` on your implementation of the interface. Note that this is not -// fired in a goroutine or a channel with workers, you should handle such -// functionality yourself if your call is non-blocking and you don't wish for -// the logging calls for levels returned from `Levels()` to block. -type Hook interface { - Levels() []Level - Fire(*Entry) error -} - -// Internal type for storing the hooks on a logger instance. -type levelHooks map[Level][]Hook - -// Add a hook to an instance of logger. This is called with -// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. -func (hooks levelHooks) Add(hook Hook) { - for _, level := range hook.Levels() { - hooks[level] = append(hooks[level], hook) - } -} - -// Fire all the hooks for the passed level. Used by `entry.log` to fire -// appropriate hooks for a log entry. -func (hooks levelHooks) Fire(level Level, entry *Entry) error { - for _, hook := range hooks[level] { - if err := hook.Fire(entry); err != nil { - return err - } - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go deleted file mode 100644 index 880d21ecdc..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go +++ /dev/null @@ -1,54 +0,0 @@ -package logrus_airbrake - -import ( - "github.com/Sirupsen/logrus" - "github.com/tobi/airbrake-go" -) - -// AirbrakeHook to send exceptions to an exception-tracking service compatible -// with the Airbrake API. You must set: -// * airbrake.Endpoint -// * airbrake.ApiKey -// * airbrake.Environment (only sends exceptions when set to "production") -// -// Before using this hook, to send an error. Entries that trigger an Error, -// Fatal or Panic should now include an "error" field to send to Airbrake. -type AirbrakeHook struct{} - -func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error { - if entry.Data["error"] == nil { - entry.Logger.WithFields(logrus.Fields{ - "source": "airbrake", - "endpoint": airbrake.Endpoint, - }).Warn("Exceptions sent to Airbrake must have an 'error' key with the error") - return nil - } - - err, ok := entry.Data["error"].(error) - if !ok { - entry.Logger.WithFields(logrus.Fields{ - "source": "airbrake", - "endpoint": airbrake.Endpoint, - }).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`") - return nil - } - - airErr := airbrake.Notify(err) - if airErr != nil { - entry.Logger.WithFields(logrus.Fields{ - "source": "airbrake", - "endpoint": airbrake.Endpoint, - "error": airErr, - }).Warn("Failed to send error to Airbrake") - } - - return nil -} - -func (hook *AirbrakeHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.ErrorLevel, - logrus.FatalLevel, - logrus.PanicLevel, - } -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md deleted file mode 100644 index ae61e9229a..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Papertrail Hook for Logrus :walrus: - -[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts). - -In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible. - -## Usage - -You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`. - -For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs. - -```go -import ( - "log/syslog" - "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/papertrail" -) - -func main() { - log := logrus.New() - hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME) - - if err == nil { - log.Hooks.Add(hook) - } -} -``` diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go deleted file mode 100644 index 48e2feaeb5..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go +++ /dev/null @@ -1,54 +0,0 @@ -package logrus_papertrail - -import ( - "fmt" - "net" - "os" - "time" - - "github.com/Sirupsen/logrus" -) - -const ( - format = "Jan 2 15:04:05" -) - -// PapertrailHook to send logs to a logging service compatible with the Papertrail API. -type PapertrailHook struct { - Host string - Port int - AppName string - UDPConn net.Conn -} - -// NewPapertrailHook creates a hook to be added to an instance of logger. -func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) { - conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port)) - return &PapertrailHook{host, port, appName, conn}, err -} - -// Fire is called when a log event is fired. -func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { - date := time.Now().Format(format) - payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message) - - bytesWritten, err := hook.UDPConn.Write([]byte(payload)) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) - return err - } - - return nil -} - -// Levels returns the available logging levels. -func (hook *PapertrailHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - logrus.WarnLevel, - logrus.InfoLevel, - logrus.DebugLevel, - } -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go deleted file mode 100644 index 96318d0030..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package logrus_papertrail - -import ( - "fmt" - "testing" - - "github.com/Sirupsen/logrus" - "github.com/stvp/go-udp-testing" -) - -func TestWritingToUDP(t *testing.T) { - port := 16661 - udp.SetAddr(fmt.Sprintf(":%d", port)) - - hook, err := NewPapertrailHook("localhost", port, "test") - if err != nil { - t.Errorf("Unable to connect to local UDP server.") - } - - log := logrus.New() - log.Hooks.Add(hook) - - udp.ShouldReceive(t, "foo", func() { - log.Info("foo") - }) -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md deleted file mode 100644 index cd706bc1b1..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Syslog Hooks for Logrus :walrus: - -## Usage - -```go -import ( - "log/syslog" - "github.com/Sirupsen/logrus" - "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) - } -} -``` \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go deleted file mode 100644 index b6fa374628..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go +++ /dev/null @@ -1,59 +0,0 @@ -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/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go deleted file mode 100644 index 42762dc10d..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go +++ /dev/null @@ -1,26 +0,0 @@ -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/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go deleted file mode 100644 index 9d11b642d4..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go +++ /dev/null @@ -1,22 +0,0 @@ -package logrus - -import ( - "encoding/json" - "fmt" - "time" -) - -type JSONFormatter struct{} - -func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - prefixFieldClashes(entry) - entry.Data["time"] = entry.Time.Format(time.RFC3339) - entry.Data["msg"] = entry.Message - entry.Data["level"] = entry.Level.String() - - 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/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go deleted file mode 100644 index 7374fe365d..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go +++ /dev/null @@ -1,161 +0,0 @@ -package logrus - -import ( - "io" - "os" - "sync" -) - -type Logger struct { - // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a - // file, or leave it default which is `os.Stdout`. You can also set this to - // something more adventorous, such as logging to Kafka. - Out io.Writer - // Hooks for the logger instance. These allow firing events based on logging - // levels and log entries. For example, to send errors to an error tracking - // service, log to StatsD or dump the core on fatal errors. - Hooks levelHooks - // All log entries pass through the formatter before logged to Out. The - // included formatters are `TextFormatter` and `JSONFormatter` for which - // TextFormatter is the default. In development (when a TTY is attached) it - // logs with colors, but to a file it wouldn't. You can easily implement your - // own that implements the `Formatter` interface, see the `README` or included - // formatters for examples. - Formatter Formatter - // The logging level the logger should log at. This is typically (and defaults - // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be - // logged. `logrus.Debug` is useful in - Level Level - // Used to sync writing to the log. - mu sync.Mutex -} - -// Creates a new logger. Configuration should be set by changing `Formatter`, -// `Out` and `Hooks` directly on the default logger instance. You can also just -// instantiate your own: -// -// var log = &Logger{ -// Out: os.Stderr, -// Formatter: new(JSONFormatter), -// Hooks: make(levelHooks), -// Level: logrus.Debug, -// } -// -// It's recommended to make this a global instance called `log`. -func New() *Logger { - return &Logger{ - Out: os.Stdout, - Formatter: new(TextFormatter), - Hooks: make(levelHooks), - Level: InfoLevel, - } -} - -// Adds a field to the log entry, note that you it doesn't log until you call -// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. -// Ff you want multiple fields, use `WithFields`. -func (logger *Logger) WithField(key string, value interface{}) *Entry { - return NewEntry(logger).WithField(key, value) -} - -// Adds a struct of fields to the log entry. All it does is call `WithField` for -// each `Field`. -func (logger *Logger) WithFields(fields Fields) *Entry { - return NewEntry(logger).WithFields(fields) -} - -func (logger *Logger) Debugf(format string, args ...interface{}) { - NewEntry(logger).Debugf(format, args...) -} - -func (logger *Logger) Infof(format string, args ...interface{}) { - NewEntry(logger).Infof(format, args...) -} - -func (logger *Logger) Printf(format string, args ...interface{}) { - NewEntry(logger).Printf(format, args...) -} - -func (logger *Logger) Warnf(format string, args ...interface{}) { - NewEntry(logger).Warnf(format, args...) -} - -func (logger *Logger) Warningf(format string, args ...interface{}) { - NewEntry(logger).Warnf(format, args...) -} - -func (logger *Logger) Errorf(format string, args ...interface{}) { - NewEntry(logger).Errorf(format, args...) -} - -func (logger *Logger) Fatalf(format string, args ...interface{}) { - NewEntry(logger).Fatalf(format, args...) -} - -func (logger *Logger) Panicf(format string, args ...interface{}) { - NewEntry(logger).Panicf(format, args...) -} - -func (logger *Logger) Debug(args ...interface{}) { - NewEntry(logger).Debug(args...) -} - -func (logger *Logger) Info(args ...interface{}) { - NewEntry(logger).Info(args...) -} - -func (logger *Logger) Print(args ...interface{}) { - NewEntry(logger).Info(args...) -} - -func (logger *Logger) Warn(args ...interface{}) { - NewEntry(logger).Warn(args...) -} - -func (logger *Logger) Warning(args ...interface{}) { - NewEntry(logger).Warn(args...) -} - -func (logger *Logger) Error(args ...interface{}) { - NewEntry(logger).Error(args...) -} - -func (logger *Logger) Fatal(args ...interface{}) { - NewEntry(logger).Fatal(args...) -} - -func (logger *Logger) Panic(args ...interface{}) { - NewEntry(logger).Panic(args...) -} - -func (logger *Logger) Debugln(args ...interface{}) { - NewEntry(logger).Debugln(args...) -} - -func (logger *Logger) Infoln(args ...interface{}) { - NewEntry(logger).Infoln(args...) -} - -func (logger *Logger) Println(args ...interface{}) { - NewEntry(logger).Println(args...) -} - -func (logger *Logger) Warnln(args ...interface{}) { - NewEntry(logger).Warnln(args...) -} - -func (logger *Logger) Warningln(args ...interface{}) { - NewEntry(logger).Warnln(args...) -} - -func (logger *Logger) Errorln(args ...interface{}) { - NewEntry(logger).Errorln(args...) -} - -func (logger *Logger) Fatalln(args ...interface{}) { - NewEntry(logger).Fatalln(args...) -} - -func (logger *Logger) Panicln(args ...interface{}) { - NewEntry(logger).Panicln(args...) -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go deleted file mode 100644 index 43ee12e90e..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go +++ /dev/null @@ -1,94 +0,0 @@ -package logrus - -import ( - "fmt" - "log" -) - -// Fields type, used to pass to `WithFields`. -type Fields map[string]interface{} - -// Level type -type Level uint8 - -// Convert the Level to a string. E.g. PanicLevel becomes "panic". -func (level Level) String() string { - switch level { - case DebugLevel: - return "debug" - case InfoLevel: - return "info" - case WarnLevel: - return "warning" - case ErrorLevel: - return "error" - case FatalLevel: - return "fatal" - case PanicLevel: - return "panic" - } - - return "unknown" -} - -// ParseLevel takes a string level and returns the Logrus log level constant. -func ParseLevel(lvl string) (Level, error) { - switch lvl { - case "panic": - return PanicLevel, nil - case "fatal": - return FatalLevel, nil - case "error": - return ErrorLevel, nil - case "warn", "warning": - return WarnLevel, nil - case "info": - return InfoLevel, nil - case "debug": - return DebugLevel, nil - } - - var l Level - return l, fmt.Errorf("not a valid logrus Level: %q", lvl) -} - -// These are the different logging levels. You can set the logging level to log -// on your instance of logger, obtained with `logrus.New()`. -const ( - // PanicLevel level, highest level of severity. Logs and then calls panic with the - // message passed to Debug, Info, ... - PanicLevel Level = iota - // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the - // logging level is set to Panic. - FatalLevel - // ErrorLevel level. Logs. Used for errors that should definitely be noted. - // Commonly used for hooks to send errors to an error tracking service. - ErrorLevel - // WarnLevel level. Non-critical entries that deserve eyes. - WarnLevel - // InfoLevel level. General operational entries about what's going on inside the - // application. - InfoLevel - // DebugLevel level. Usually only enabled when debugging. Very verbose logging. - DebugLevel -) - -// Won't compile if StdLogger can't be realized by a log.Logger -var _ StdLogger = &log.Logger{} - -// StdLogger is what your logrus-enabled library should take, that way -// it'll accept a stdlib logger and a logrus logger. There's no standard -// interface, this is the closest we get, unfortunately. -type StdLogger interface { - Print(...interface{}) - Printf(string, ...interface{}) - Println(...interface{}) - - Fatal(...interface{}) - Fatalf(string, ...interface{}) - Fatalln(...interface{}) - - Panic(...interface{}) - Panicf(string, ...interface{}) - Panicln(...interface{}) -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go deleted file mode 100644 index 15157d172d..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package logrus - -import ( - "bytes" - "encoding/json" - "strconv" - "strings" - "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, err := strconv.Unquote(kvArr[1]) - 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) - }) -} - -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 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()) -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go deleted file mode 100644 index 8fe02a4aec..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go +++ /dev/null @@ -1,12 +0,0 @@ -// 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.TIOCGETA - -type Termios syscall.Termios diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go deleted file mode 100644 index 0428ee5d52..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. -*/ -package logrus - -import ( - "syscall" -) - -const ioctlReadTermios = syscall.TIOCGETA - -type Termios struct { - Iflag uint32 - Oflag uint32 - Cflag uint32 - Lflag uint32 - Cc [20]uint8 - Ispeed uint32 - Ospeed uint32 -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go deleted file mode 100644 index a2c0b40db6..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go +++ /dev/null @@ -1,12 +0,0 @@ -// 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/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go deleted file mode 100644 index 276447bd5c..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go +++ /dev/null @@ -1,21 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux,!appengine darwin freebsd - -package logrus - -import ( - "syscall" - "unsafe" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal() bool { - fd := syscall.Stdout - var termios Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go deleted file mode 100644 index 2e09f6f7e3..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go +++ /dev/null @@ -1,27 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package logrus - -import ( - "syscall" - "unsafe" -) - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal() bool { - fd := syscall.Stdout - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go deleted file mode 100644 index fc0a4082a7..0000000000 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go +++ /dev/null @@ -1,95 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "sort" - "strings" - "time" -) - -const ( - nocolor = 0 - red = 31 - green = 32 - yellow = 33 - blue = 34 -) - -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 - DisableColors bool -} - -func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - - var keys []string - for k := range entry.Data { - keys = append(keys, k) - } - sort.Strings(keys) - - b := &bytes.Buffer{} - - prefixFieldClashes(entry) - - isColored := (f.ForceColors || isTerminal) && !f.DisableColors - - if isColored { - printColored(b, entry, keys) - } else { - f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339)) - f.appendKeyValue(b, "level", entry.Level.String()) - f.appendKeyValue(b, "msg", entry.Message) - for _, key := range keys { - f.appendKeyValue(b, key, entry.Data[key]) - } - } - - b.WriteByte('\n') - return b.Bytes(), nil -} - -func printColored(b *bytes.Buffer, entry *Entry, keys []string) { - var levelColor int - switch entry.Level { - case WarnLevel: - levelColor = yellow - case ErrorLevel, FatalLevel, PanicLevel: - levelColor = red - default: - levelColor = blue - } - - levelText := strings.ToUpper(entry.Level.String())[0:4] - - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) - for _, k := range keys { - v := entry.Data[k] - fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) - } -} - -func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { - switch value.(type) { - case string, error: - fmt.Fprintf(b, "%v=%q ", key, value) - default: - fmt.Fprintf(b, "%v=%v ", key, value) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/MAINTAINERS b/Godeps/_workspace/src/github.com/docker/docker/api/MAINTAINERS deleted file mode 100644 index 96abeae570..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/MAINTAINERS +++ /dev/null @@ -1,2 +0,0 @@ -Victor Vieux (@vieux) -Jessie Frazelle (@jfrazelle) diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/README.md b/Godeps/_workspace/src/github.com/docker/docker/api/README.md deleted file mode 100644 index 453f61a1a1..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/README.md +++ /dev/null @@ -1,5 +0,0 @@ -This directory contains code pertaining to the Docker API: - - - Used by the docker client when communicating with the docker daemon - - - Used by third party tools wishing to interface with the docker daemon diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/api_unit_test.go b/Godeps/_workspace/src/github.com/docker/docker/api/api_unit_test.go deleted file mode 100644 index 678331d369..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/api_unit_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package api - -import ( - "testing" -) - -func TestJsonContentType(t *testing.T) { - if !MatchesContentType("application/json", "application/json") { - t.Fail() - } - - if !MatchesContentType("application/json; charset=utf-8", "application/json") { - t.Fail() - } - - if MatchesContentType("dockerapplication/json", "application/json") { - t.Fail() - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/client/cli.go b/Godeps/_workspace/src/github.com/docker/docker/api/client/cli.go deleted file mode 100644 index 5e1ccb3291..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/client/cli.go +++ /dev/null @@ -1,188 +0,0 @@ -package client - -import ( - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "net" - "net/http" - "os" - "reflect" - "strings" - "text/template" - "time" - - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/pkg/term" - "github.com/docker/docker/registry" -) - -type DockerCli struct { - proto string - addr string - configFile *registry.ConfigFile - in io.ReadCloser - out io.Writer - err io.Writer - keyFile string - tlsConfig *tls.Config - scheme string - // inFd holds file descriptor of the client's STDIN, if it's a valid file - inFd uintptr - // outFd holds file descriptor of the client's STDOUT, if it's a valid file - outFd uintptr - // isTerminalIn describes if client's STDIN is a TTY - isTerminalIn bool - // isTerminalOut describes if client's STDOUT is a TTY - isTerminalOut bool - transport *http.Transport -} - -var funcMap = template.FuncMap{ - "json": func(v interface{}) string { - a, _ := json.Marshal(v) - return string(a) - }, -} - -func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { - camelArgs := make([]string, len(args)) - for i, s := range args { - if len(s) == 0 { - return nil, false - } - camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) - } - methodName := "Cmd" + strings.Join(camelArgs, "") - method := reflect.ValueOf(cli).MethodByName(methodName) - if !method.IsValid() { - return nil, false - } - return method.Interface().(func(...string) error), true -} - -// Cmd executes the specified command -func (cli *DockerCli) Cmd(args ...string) error { - if len(args) > 1 { - method, exists := cli.getMethod(args[:2]...) - if exists { - return method(args[2:]...) - } - } - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0]) - os.Exit(1) - } - return method(args[1:]...) - } - return cli.CmdHelp() -} - -func (cli *DockerCli) Subcmd(name, signature, description string, exitOnError bool) *flag.FlagSet { - var errorHandling flag.ErrorHandling - if exitOnError { - errorHandling = flag.ExitOnError - } else { - errorHandling = flag.ContinueOnError - } - flags := flag.NewFlagSet(name, errorHandling) - flags.Usage = func() { - options := "" - if flags.FlagCountUndeprecated() > 0 { - options = "[OPTIONS] " - } - fmt.Fprintf(cli.out, "\nUsage: docker %s %s%s\n\n%s\n\n", name, options, signature, description) - flags.SetOutput(cli.out) - flags.PrintDefaults() - os.Exit(0) - } - return flags -} - -func (cli *DockerCli) LoadConfigFile() (err error) { - cli.configFile, err = registry.LoadConfig(os.Getenv("HOME")) - if err != nil { - fmt.Fprintf(cli.err, "WARNING: %s\n", err) - } - return err -} - -func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { - // In order to attach to a container tty, input stream for the client must - // be a tty itself: redirecting or piping the client standard input is - // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. - if ttyMode && attachStdin && !cli.isTerminalIn { - return errors.New("cannot enable tty mode on non tty input") - } - return nil -} - -func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli { - var ( - inFd uintptr - outFd uintptr - isTerminalIn = false - isTerminalOut = false - scheme = "http" - ) - - if tlsConfig != nil { - scheme = "https" - } - - if in != nil { - if file, ok := in.(*os.File); ok { - inFd = file.Fd() - isTerminalIn = term.IsTerminal(inFd) - } - } - - if out != nil { - if file, ok := out.(*os.File); ok { - outFd = file.Fd() - isTerminalOut = term.IsTerminal(outFd) - } - } - - if err == nil { - err = out - } - - // The transport is created here for reuse during the client session - tr := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: tlsConfig, - } - - // Why 32? See issue 8035 - timeout := 32 * time.Second - if proto == "unix" { - // no need in compressing for local communications - tr.DisableCompression = true - tr.Dial = func(_, _ string) (net.Conn, error) { - return net.DialTimeout(proto, addr, timeout) - } - } else { - tr.Dial = (&net.Dialer{Timeout: timeout}).Dial - } - - return &DockerCli{ - proto: proto, - addr: addr, - in: in, - out: out, - err: err, - keyFile: keyFile, - inFd: inFd, - outFd: outFd, - isTerminalIn: isTerminalIn, - isTerminalOut: isTerminalOut, - tlsConfig: tlsConfig, - scheme: scheme, - transport: tr, - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/client/commands.go b/Godeps/_workspace/src/github.com/docker/docker/api/client/commands.go deleted file mode 100644 index c4ce5e01f3..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/client/commands.go +++ /dev/null @@ -1,2764 +0,0 @@ -package client - -import ( - "bufio" - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "text/tabwriter" - "text/template" - "time" - - log "github.com/Sirupsen/logrus" - "github.com/docker/docker/api" - "github.com/docker/docker/api/stats" - "github.com/docker/docker/dockerversion" - "github.com/docker/docker/engine" - "github.com/docker/docker/graph" - "github.com/docker/docker/nat" - "github.com/docker/docker/opts" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/fileutils" - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/parsers/filters" - "github.com/docker/docker/pkg/promise" - "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/pkg/symlink" - "github.com/docker/docker/pkg/term" - "github.com/docker/docker/pkg/timeutils" - "github.com/docker/docker/pkg/units" - "github.com/docker/docker/pkg/urlutil" - "github.com/docker/docker/registry" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/utils" -) - -const ( - tarHeaderSize = 512 -) - -func (cli *DockerCli) CmdHelp(args ...string) error { - if len(args) > 1 { - method, exists := cli.getMethod(args[:2]...) - if exists { - method("--help") - return nil - } - } - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0]) - os.Exit(1) - } else { - method("--help") - return nil - } - } - - flag.Usage() - - return nil -} - -func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true) - tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") - suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers") - noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") - rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build") - forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds") - pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image") - dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile(Default is 'Dockerfile' at context root)") - - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - var ( - context archive.Archive - isRemote bool - err error - ) - - _, err = exec.LookPath("git") - hasGit := err == nil - if cmd.Arg(0) == "-" { - // As a special case, 'docker build -' will build from either an empty context with the - // contents of stdin as a Dockerfile, or a tar-ed context from stdin. - buf := bufio.NewReader(cli.in) - magic, err := buf.Peek(tarHeaderSize) - if err != nil && err != io.EOF { - return fmt.Errorf("failed to peek context header from STDIN: %v", err) - } - if !archive.IsArchive(magic) { - dockerfile, err := ioutil.ReadAll(buf) - if err != nil { - return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err) - } - if *dockerfileName == "" { - *dockerfileName = api.DefaultDockerfileName - } - context, err = archive.Generate(*dockerfileName, string(dockerfile)) - } else { - context = ioutil.NopCloser(buf) - } - } else if urlutil.IsURL(cmd.Arg(0)) && (!urlutil.IsGitURL(cmd.Arg(0)) || !hasGit) { - isRemote = true - } else { - root := cmd.Arg(0) - if urlutil.IsGitURL(root) { - remoteURL := cmd.Arg(0) - if !urlutil.IsGitTransport(remoteURL) { - remoteURL = "https://" + remoteURL - } - - root, err = ioutil.TempDir("", "docker-build-git") - if err != nil { - return err - } - defer os.RemoveAll(root) - - if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil { - return fmt.Errorf("Error trying to use git: %s (%s)", err, output) - } - } - if _, err := os.Stat(root); err != nil { - return err - } - - absRoot, err := filepath.Abs(root) - if err != nil { - return err - } - - filename := *dockerfileName // path to Dockerfile - - if *dockerfileName == "" { - // No -f/--file was specified so use the default - *dockerfileName = api.DefaultDockerfileName - filename = path.Join(absRoot, *dockerfileName) - } - - origDockerfile := *dockerfileName // used for error msg - - if filename, err = filepath.Abs(filename); err != nil { - return err - } - - // Verify that 'filename' is within the build context - filename, err = symlink.FollowSymlinkInScope(filename, absRoot) - if err != nil { - return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root) - } - - // Now reset the dockerfileName to be relative to the build context - *dockerfileName, err = filepath.Rel(absRoot, filename) - if err != nil { - return err - } - - if _, err = os.Lstat(filename); os.IsNotExist(err) { - return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile) - } - var includes = []string{"."} - - excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore")) - if err != nil { - return err - } - - // If .dockerignore mentions .dockerignore or the Dockerfile - // then make sure we send both files over to the daemon - // because Dockerfile is, obviously, needed no matter what, and - // .dockerignore is needed to know if either one needs to be - // removed. The deamon will remove them for us, if needed, after it - // parses the Dockerfile. - keepThem1, _ := fileutils.Matches(".dockerignore", excludes) - keepThem2, _ := fileutils.Matches(*dockerfileName, excludes) - if keepThem1 || keepThem2 { - includes = append(includes, ".dockerignore", *dockerfileName) - } - - if err = utils.ValidateContextDirectory(root, excludes); err != nil { - return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err) - } - options := &archive.TarOptions{ - Compression: archive.Uncompressed, - ExcludePatterns: excludes, - IncludeFiles: includes, - } - context, err = archive.TarWithOptions(root, options) - if err != nil { - return err - } - } - var body io.Reader - // Setup an upload progress bar - // FIXME: ProgressReader shouldn't be this annoying to use - if context != nil { - sf := utils.NewStreamFormatter(false) - body = utils.ProgressReader(context, 0, cli.out, sf, true, "", "Sending build context to Docker daemon") - } - // Send the build context - v := &url.Values{} - - //Check if the given image name can be resolved - if *tag != "" { - repository, tag := parsers.ParseRepositoryTag(*tag) - if err := registry.ValidateRepositoryName(repository); err != nil { - return err - } - if len(tag) > 0 { - if err := graph.ValidateTagName(tag); err != nil { - return err - } - } - } - - v.Set("t", *tag) - - if *suppressOutput { - v.Set("q", "1") - } - if isRemote { - v.Set("remote", cmd.Arg(0)) - } - if *noCache { - v.Set("nocache", "1") - } - if *rm { - v.Set("rm", "1") - } else { - v.Set("rm", "0") - } - - if *forceRm { - v.Set("forcerm", "1") - } - - if *pull { - v.Set("pull", "1") - } - - v.Set("dockerfile", *dockerfileName) - - cli.LoadConfigFile() - - headers := http.Header(make(map[string][]string)) - buf, err := json.Marshal(cli.configFile) - if err != nil { - return err - } - headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) - - if context != nil { - headers.Set("Content-Type", "application/tar") - } - err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers) - if jerr, ok := err.(*utils.JSONError); ok { - // If no error code is set, default to 1 - if jerr.Code == 0 { - jerr.Code = 1 - } - return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code} - } - return err -} - -// 'docker login': login / register a user to registry service. -func (cli *DockerCli) CmdLogin(args ...string) error { - cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true) - cmd.Require(flag.Max, 1) - - var username, password, email string - - cmd.StringVar(&username, []string{"u", "-username"}, "", "Username") - cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") - cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") - - utils.ParseFlags(cmd, args, true) - - serverAddress := registry.IndexServerAddress() - if len(cmd.Args()) > 0 { - serverAddress = cmd.Arg(0) - } - - promptDefault := func(prompt string, configDefault string) { - if configDefault == "" { - fmt.Fprintf(cli.out, "%s: ", prompt) - } else { - fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) - } - } - - readInput := func(in io.Reader, out io.Writer) string { - reader := bufio.NewReader(in) - line, _, err := reader.ReadLine() - if err != nil { - fmt.Fprintln(out, err.Error()) - os.Exit(1) - } - return string(line) - } - - cli.LoadConfigFile() - authconfig, ok := cli.configFile.Configs[serverAddress] - if !ok { - authconfig = registry.AuthConfig{} - } - - if username == "" { - promptDefault("Username", authconfig.Username) - username = readInput(cli.in, cli.out) - if username == "" { - username = authconfig.Username - } - } - // Assume that a different username means they may not want to use - // the password or email from the config file, so prompt them - if username != authconfig.Username { - if password == "" { - oldState, err := term.SaveState(cli.inFd) - if err != nil { - return err - } - fmt.Fprintf(cli.out, "Password: ") - term.DisableEcho(cli.inFd, oldState) - - password = readInput(cli.in, cli.out) - fmt.Fprint(cli.out, "\n") - - term.RestoreTerminal(cli.inFd, oldState) - if password == "" { - return fmt.Errorf("Error : Password Required") - } - } - - if email == "" { - promptDefault("Email", authconfig.Email) - email = readInput(cli.in, cli.out) - if email == "" { - email = authconfig.Email - } - } - } else { - // However, if they don't override the username use the - // password or email from the cmd line if specified. IOW, allow - // then to change/overide them. And if not specified, just - // use what's in the config file - if password == "" { - password = authconfig.Password - } - if email == "" { - email = authconfig.Email - } - } - authconfig.Username = username - authconfig.Password = password - authconfig.Email = email - authconfig.ServerAddress = serverAddress - cli.configFile.Configs[serverAddress] = authconfig - - stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false) - if statusCode == 401 { - delete(cli.configFile.Configs, serverAddress) - registry.SaveConfig(cli.configFile) - return err - } - if err != nil { - return err - } - var out2 engine.Env - err = out2.Decode(stream) - if err != nil { - cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME")) - return err - } - registry.SaveConfig(cli.configFile) - if out2.Get("Status") != "" { - fmt.Fprintf(cli.out, "%s\n", out2.Get("Status")) - } - return nil -} - -// log out from a Docker registry -func (cli *DockerCli) CmdLogout(args ...string) error { - cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true) - cmd.Require(flag.Max, 1) - - utils.ParseFlags(cmd, args, false) - serverAddress := registry.IndexServerAddress() - if len(cmd.Args()) > 0 { - serverAddress = cmd.Arg(0) - } - - cli.LoadConfigFile() - if _, ok := cli.configFile.Configs[serverAddress]; !ok { - fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress) - } else { - fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress) - delete(cli.configFile.Configs, serverAddress) - - if err := registry.SaveConfig(cli.configFile); err != nil { - return fmt.Errorf("Failed to save docker config: %v", err) - } - } - return nil -} - -// 'docker wait': block until a container stops -func (cli *DockerCli) CmdWait(args ...string) error { - cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.", true) - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - var encounteredError error - for _, name := range cmd.Args() { - status, err := waitForExit(cli, name) - if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to wait one or more containers") - } else { - fmt.Fprintf(cli.out, "%d\n", status) - } - } - return encounteredError -} - -// 'docker version': show version information -func (cli *DockerCli) CmdVersion(args ...string) error { - cmd := cli.Subcmd("version", "", "Show the Docker version information.", true) - cmd.Require(flag.Exact, 0) - - utils.ParseFlags(cmd, args, false) - - if dockerversion.VERSION != "" { - fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION) - } - fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION) - fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version()) - if dockerversion.GITCOMMIT != "" { - fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT) - } - fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH) - - body, _, err := readBody(cli.call("GET", "/version", nil, false)) - if err != nil { - return err - } - - out := engine.NewOutput() - remoteVersion, err := out.AddEnv() - if err != nil { - log.Errorf("Error reading remote version: %s", err) - return err - } - if _, err := out.Write(body); err != nil { - log.Errorf("Error reading remote version: %s", err) - return err - } - out.Close() - fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version")) - if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" { - fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion) - } - fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion")) - fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit")) - return nil -} - -// 'docker info': display system-wide information. -func (cli *DockerCli) CmdInfo(args ...string) error { - cmd := cli.Subcmd("info", "", "Display system-wide information", true) - cmd.Require(flag.Exact, 0) - utils.ParseFlags(cmd, args, false) - - body, _, err := readBody(cli.call("GET", "/info", nil, false)) - if err != nil { - return err - } - - out := engine.NewOutput() - remoteInfo, err := out.AddEnv() - if err != nil { - return err - } - - if _, err := out.Write(body); err != nil { - log.Errorf("Error reading remote info: %s", err) - return err - } - out.Close() - - if remoteInfo.Exists("Containers") { - fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers")) - } - if remoteInfo.Exists("Images") { - fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images")) - } - if remoteInfo.Exists("Driver") { - fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver")) - } - if remoteInfo.Exists("DriverStatus") { - var driverStatus [][2]string - if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil { - return err - } - for _, pair := range driverStatus { - fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1]) - } - } - if remoteInfo.Exists("ExecutionDriver") { - fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver")) - } - if remoteInfo.Exists("KernelVersion") { - fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion")) - } - if remoteInfo.Exists("OperatingSystem") { - fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem")) - } - if remoteInfo.Exists("NCPU") { - fmt.Fprintf(cli.out, "CPUs: %d\n", remoteInfo.GetInt("NCPU")) - } - if remoteInfo.Exists("MemTotal") { - fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(remoteInfo.GetInt64("MemTotal")))) - } - if remoteInfo.Exists("Name") { - fmt.Fprintf(cli.out, "Name: %s\n", remoteInfo.Get("Name")) - } - if remoteInfo.Exists("ID") { - fmt.Fprintf(cli.out, "ID: %s\n", remoteInfo.Get("ID")) - } - - if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" { - if remoteInfo.Exists("Debug") { - fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug")) - } - fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "") - if remoteInfo.Exists("NFd") { - fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd")) - } - if remoteInfo.Exists("NGoroutines") { - fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines")) - } - if remoteInfo.Exists("NEventsListener") { - fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener")) - } - if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" { - fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1) - } - if initPath := remoteInfo.Get("InitPath"); initPath != "" { - fmt.Fprintf(cli.out, "Init Path: %s\n", initPath) - } - if root := remoteInfo.Get("DockerRootDir"); root != "" { - fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", root) - } - } - - if len(remoteInfo.GetList("IndexServerAddress")) != 0 { - cli.LoadConfigFile() - u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username - if len(u) > 0 { - fmt.Fprintf(cli.out, "Username: %v\n", u) - fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress")) - } - } - if remoteInfo.Exists("MemoryLimit") && !remoteInfo.GetBool("MemoryLimit") { - fmt.Fprintf(cli.err, "WARNING: No memory limit support\n") - } - if remoteInfo.Exists("SwapLimit") && !remoteInfo.GetBool("SwapLimit") { - fmt.Fprintf(cli.err, "WARNING: No swap limit support\n") - } - if remoteInfo.Exists("IPv4Forwarding") && !remoteInfo.GetBool("IPv4Forwarding") { - fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n") - } - if remoteInfo.Exists("Labels") { - fmt.Fprintln(cli.out, "Labels:") - for _, attribute := range remoteInfo.GetList("Labels") { - fmt.Fprintf(cli.out, " %s\n", attribute) - } - } - - return nil -} - -func (cli *DockerCli) CmdStop(args ...string) error { - cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period", true) - nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.") - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - v := url.Values{} - v.Set("t", strconv.Itoa(*nSeconds)) - - var encounteredError error - for _, name := range cmd.Args() { - _, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false)) - if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to stop one or more containers") - } else { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - return encounteredError -} - -func (cli *DockerCli) CmdRestart(args ...string) error { - cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true) - nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.") - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - v := url.Values{} - v.Set("t", strconv.Itoa(*nSeconds)) - - var encounteredError error - for _, name := range cmd.Args() { - _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false)) - if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to restart one or more containers") - } else { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - return encounteredError -} - -func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal { - sigc := make(chan os.Signal, 128) - signal.CatchAll(sigc) - go func() { - for s := range sigc { - if s == signal.SIGCHLD { - continue - } - var sig string - for sigStr, sigN := range signal.SignalMap { - if sigN == s { - sig = sigStr - break - } - } - if sig == "" { - log.Errorf("Unsupported signal: %v. Discarding.", s) - } - if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil { - log.Debugf("Error sending signal: %s", err) - } - } - }() - return sigc -} - -func (cli *DockerCli) CmdStart(args ...string) error { - var ( - cErr chan error - tty bool - - cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container", true) - attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process") - openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN") - ) - - cmd.Require(flag.Min, 1) - utils.ParseFlags(cmd, args, true) - - hijacked := make(chan io.Closer) - - if *attach || *openStdin { - if cmd.NArg() > 1 { - return fmt.Errorf("You cannot start and attach multiple containers at once.") - } - - stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) - if err != nil { - return err - } - - env := engine.Env{} - if err := env.Decode(stream); err != nil { - return err - } - config := env.GetSubEnv("Config") - tty = config.GetBool("Tty") - - if !tty { - sigc := cli.forwardAllSignals(cmd.Arg(0)) - defer signal.StopCatch(sigc) - } - - var in io.ReadCloser - - v := url.Values{} - v.Set("stream", "1") - - if *openStdin && config.GetBool("OpenStdin") { - v.Set("stdin", "1") - in = cli.in - } - - v.Set("stdout", "1") - v.Set("stderr", "1") - - cErr = promise.Go(func() error { - return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil) - }) - } else { - close(hijacked) - } - - // Acknowledge the hijack before starting - select { - case closer := <-hijacked: - // Make sure that the hijack gets closed when returning (results - // in closing the hijack chan and freeing server's goroutines) - if closer != nil { - defer closer.Close() - } - case err := <-cErr: - if err != nil { - return err - } - } - - var encounteredError error - for _, name := range cmd.Args() { - _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false)) - if err != nil { - if !*attach && !*openStdin { - fmt.Fprintf(cli.err, "%s\n", err) - } - encounteredError = fmt.Errorf("Error: failed to start one or more containers") - } else { - if !*attach && !*openStdin { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - } - if encounteredError != nil { - if *openStdin || *attach { - cli.in.Close() - } - return encounteredError - } - - if *openStdin || *attach { - if tty && cli.isTerminalOut { - if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { - log.Errorf("Error monitoring TTY size: %s", err) - } - } - if attchErr := <-cErr; attchErr != nil { - return attchErr - } - _, status, err := getExitCode(cli, cmd.Arg(0)) - if err != nil { - return err - } - if status != 0 { - return &utils.StatusError{StatusCode: status} - } - } - return nil -} - -func (cli *DockerCli) CmdUnpause(args ...string) error { - cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container", true) - cmd.Require(flag.Exact, 1) - utils.ParseFlags(cmd, args, false) - - var encounteredError error - for _, name := range cmd.Args() { - if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name) - } else { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - return encounteredError -} - -func (cli *DockerCli) CmdPause(args ...string) error { - cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container", true) - cmd.Require(flag.Exact, 1) - utils.ParseFlags(cmd, args, false) - - var encounteredError error - for _, name := range cmd.Args() { - if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to pause container named %s", name) - } else { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - return encounteredError -} - -func (cli *DockerCli) CmdRename(args ...string) error { - cmd := cli.Subcmd("rename", "OLD_NAME NEW_NAME", "Rename a container", true) - if err := cmd.Parse(args); err != nil { - return nil - } - - if cmd.NArg() != 2 { - cmd.Usage() - return nil - } - old_name := cmd.Arg(0) - new_name := cmd.Arg(1) - - if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - return fmt.Errorf("Error: failed to rename container named %s", old_name) - } - return nil -} - -func (cli *DockerCli) CmdInspect(args ...string) error { - cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true) - tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.") - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - var tmpl *template.Template - if *tmplStr != "" { - var err error - if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { - fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) - return &utils.StatusError{StatusCode: 64, - Status: "Template parsing error: " + err.Error()} - } - } - - indented := new(bytes.Buffer) - indented.WriteByte('[') - status := 0 - - for _, name := range cmd.Args() { - obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false)) - if err != nil { - obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false)) - if err != nil { - if strings.Contains(err.Error(), "No such") { - fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) - } else { - fmt.Fprintf(cli.err, "%s", err) - } - status = 1 - continue - } - } - - if tmpl == nil { - if err = json.Indent(indented, obj, "", " "); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - status = 1 - continue - } - } else { - // Has template, will render - var value interface{} - if err := json.Unmarshal(obj, &value); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - status = 1 - continue - } - if err := tmpl.Execute(cli.out, value); err != nil { - return err - } - cli.out.Write([]byte{'\n'}) - } - indented.WriteString(",") - } - - if indented.Len() > 1 { - // Remove trailing ',' - indented.Truncate(indented.Len() - 1) - } - indented.WriteString("]\n") - - if tmpl == nil { - if _, err := io.Copy(cli.out, indented); err != nil { - return err - } - } - - if status != 0 { - return &utils.StatusError{StatusCode: status} - } - return nil -} - -func (cli *DockerCli) CmdTop(args ...string) error { - cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container", true) - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - val := url.Values{} - if cmd.NArg() > 1 { - val.Set("ps_args", strings.Join(cmd.Args()[1:], " ")) - } - - stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false) - if err != nil { - return err - } - var procs engine.Env - if err := procs.Decode(stream); err != nil { - return err - } - w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t")) - processes := [][]string{} - if err := procs.GetJson("Processes", &processes); err != nil { - return err - } - for _, proc := range processes { - fmt.Fprintln(w, strings.Join(proc, "\t")) - } - w.Flush() - return nil -} - -func (cli *DockerCli) CmdPort(args ...string) error { - cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT", true) - cmd.Require(flag.Min, 1) - utils.ParseFlags(cmd, args, true) - - stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) - if err != nil { - return err - } - - env := engine.Env{} - if err := env.Decode(stream); err != nil { - return err - } - ports := nat.PortMap{} - if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil { - return err - } - - if cmd.NArg() == 2 { - var ( - port = cmd.Arg(1) - proto = "tcp" - parts = strings.SplitN(port, "/", 2) - ) - - if len(parts) == 2 && len(parts[1]) != 0 { - port = parts[0] - proto = parts[1] - } - natPort := port + "/" + proto - if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { - for _, frontend := range frontends { - fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) - } - return nil - } - return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0)) - } - - for from, frontends := range ports { - for _, frontend := range frontends { - fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort) - } - } - - return nil -} - -// 'docker rmi IMAGE' removes all images with the name IMAGE -func (cli *DockerCli) CmdRmi(args ...string) error { - var ( - cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images", true) - force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image") - noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents") - ) - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - v := url.Values{} - if *force { - v.Set("force", "1") - } - if *noprune { - v.Set("noprune", "1") - } - - var encounteredError error - for _, name := range cmd.Args() { - body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false)) - if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to remove one or more images") - } else { - outs := engine.NewTable("Created", 0) - if _, err := outs.ReadListFrom(body); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to remove one or more images") - continue - } - for _, out := range outs.Data { - if out.Get("Deleted") != "" { - fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted")) - } else { - fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged")) - } - } - } - } - return encounteredError -} - -func (cli *DockerCli) CmdHistory(args ...string) error { - cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true) - quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") - noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false)) - if err != nil { - return err - } - - outs := engine.NewTable("Created", 0) - if _, err := outs.ReadListFrom(body); err != nil { - return err - } - - w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - if !*quiet { - fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE") - } - - for _, out := range outs.Data { - outID := out.Get("Id") - if !*quiet { - if *noTrunc { - fmt.Fprintf(w, "%s\t", outID) - } else { - fmt.Fprintf(w, "%s\t", utils.TruncateID(outID)) - } - - fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0)))) - - if *noTrunc { - fmt.Fprintf(w, "%s\t", out.Get("CreatedBy")) - } else { - fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45)) - } - fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("Size")))) - } else { - if *noTrunc { - fmt.Fprintln(w, outID) - } else { - fmt.Fprintln(w, utils.TruncateID(outID)) - } - } - } - w.Flush() - return nil -} - -func (cli *DockerCli) CmdRm(args ...string) error { - cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers", true) - v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container") - link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container") - force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)") - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - val := url.Values{} - if *v { - val.Set("v", "1") - } - if *link { - val.Set("link", "1") - } - - if *force { - val.Set("force", "1") - } - - var encounteredError error - for _, name := range cmd.Args() { - _, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false)) - if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to remove one or more containers") - } else { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - return encounteredError -} - -// 'docker kill NAME' kills a running container -func (cli *DockerCli) CmdKill(args ...string) error { - cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal", true) - signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container") - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - var encounteredError error - for _, name := range cmd.Args() { - if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to kill one or more containers") - } else { - fmt.Fprintf(cli.out, "%s\n", name) - } - } - return encounteredError -} - -func (cli *DockerCli) CmdImport(args ...string) error { - cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.", true) - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - var ( - v = url.Values{} - src = cmd.Arg(0) - repository = cmd.Arg(1) - ) - - v.Set("fromSrc", src) - v.Set("repo", repository) - - if cmd.NArg() == 3 { - fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' has been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n") - v.Set("tag", cmd.Arg(2)) - } - - if repository != "" { - //Check if the given image name can be resolved - repo, _ := parsers.ParseRepositoryTag(repository) - if err := registry.ValidateRepositoryName(repo); err != nil { - return err - } - } - - var in io.Reader - - if src == "-" { - in = cli.in - } - - return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil) -} - -func (cli *DockerCli) CmdPush(args ...string) error { - cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry", true) - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - name := cmd.Arg(0) - - cli.LoadConfigFile() - - remote, tag := parsers.ParseRepositoryTag(name) - - // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, err := registry.ParseRepositoryInfo(remote) - if err != nil { - return err - } - // Resolve the Auth config relevant for this server - authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) - // If we're not using a custom registry, we know the restrictions - // applied to repository names and can warn the user in advance. - // Custom repositories can have different rules, and we must also - // allow pushing by image ID. - if repoInfo.Official { - username := authConfig.Username - if username == "" { - username = "" - } - return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to / (ex: %s/%s)", username, repoInfo.LocalName) - } - - v := url.Values{} - v.Set("tag", tag) - - push := func(authConfig registry.AuthConfig) error { - buf, err := json.Marshal(authConfig) - if err != nil { - return err - } - registryAuthHeader := []string{ - base64.URLEncoding.EncodeToString(buf), - } - - return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{ - "X-Registry-Auth": registryAuthHeader, - }) - } - - if err := push(authConfig); err != nil { - if strings.Contains(err.Error(), "Status 401") { - fmt.Fprintln(cli.out, "\nPlease login prior to push:") - if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil { - return err - } - authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) - return push(authConfig) - } - return err - } - return nil -} - -func (cli *DockerCli) CmdPull(args ...string) error { - cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry", true) - allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - var ( - v = url.Values{} - remote = cmd.Arg(0) - newRemote = remote - ) - taglessRemote, tag := parsers.ParseRepositoryTag(remote) - if tag == "" && !*allTags { - newRemote = taglessRemote + ":" + graph.DEFAULTTAG - } - if tag != "" && *allTags { - return fmt.Errorf("tag can't be used with --all-tags/-a") - } - - v.Set("fromImage", newRemote) - - // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, err := registry.ParseRepositoryInfo(taglessRemote) - if err != nil { - return err - } - - cli.LoadConfigFile() - - // Resolve the Auth config relevant for this server - authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) - - pull := func(authConfig registry.AuthConfig) error { - buf, err := json.Marshal(authConfig) - if err != nil { - return err - } - registryAuthHeader := []string{ - base64.URLEncoding.EncodeToString(buf), - } - - return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{ - "X-Registry-Auth": registryAuthHeader, - }) - } - - if err := pull(authConfig); err != nil { - if strings.Contains(err.Error(), "Status 401") { - fmt.Fprintln(cli.out, "\nPlease login prior to pull:") - if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil { - return err - } - authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) - return pull(authConfig) - } - return err - } - - return nil -} - -func (cli *DockerCli) CmdImages(args ...string) error { - cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true) - quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") - all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)") - noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") - // FIXME: --viz and --tree are deprecated. Remove them in a future version. - flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format") - flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format") - - flFilter := opts.NewListOpts(nil) - cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e., 'dangling=true')") - cmd.Require(flag.Max, 1) - - utils.ParseFlags(cmd, args, true) - - // Consolidate all filter flags, and sanity check them early. - // They'll get process in the daemon/server. - imageFilterArgs := filters.Args{} - for _, f := range flFilter.GetAll() { - var err error - imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs) - if err != nil { - return err - } - } - - matchName := cmd.Arg(0) - // FIXME: --viz and --tree are deprecated. Remove them in a future version. - if *flViz || *flTree { - v := url.Values{ - "all": []string{"1"}, - } - if len(imageFilterArgs) > 0 { - filterJson, err := filters.ToParam(imageFilterArgs) - if err != nil { - return err - } - v.Set("filters", filterJson) - } - - body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false)) - if err != nil { - return err - } - - outs := engine.NewTable("Created", 0) - if _, err := outs.ReadListFrom(body); err != nil { - return err - } - - var ( - printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string) - startImage *engine.Env - - roots = engine.NewTable("Created", outs.Len()) - byParent = make(map[string]*engine.Table) - ) - - for _, image := range outs.Data { - if image.Get("ParentId") == "" { - roots.Add(image) - } else { - if children, exists := byParent[image.Get("ParentId")]; exists { - children.Add(image) - } else { - byParent[image.Get("ParentId")] = engine.NewTable("Created", 1) - byParent[image.Get("ParentId")].Add(image) - } - } - - if matchName != "" { - if matchName == image.Get("Id") || matchName == utils.TruncateID(image.Get("Id")) { - startImage = image - } - - for _, repotag := range image.GetList("RepoTags") { - if repotag == matchName { - startImage = image - } - } - } - } - - if *flViz { - fmt.Fprintf(cli.out, "digraph docker {\n") - printNode = (*DockerCli).printVizNode - } else { - printNode = (*DockerCli).printTreeNode - } - - if startImage != nil { - root := engine.NewTable("Created", 1) - root.Add(startImage) - cli.WalkTree(*noTrunc, root, byParent, "", printNode) - } else if matchName == "" { - cli.WalkTree(*noTrunc, roots, byParent, "", printNode) - } - if *flViz { - fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") - } - } else { - v := url.Values{} - if len(imageFilterArgs) > 0 { - filterJson, err := filters.ToParam(imageFilterArgs) - if err != nil { - return err - } - v.Set("filters", filterJson) - } - - if cmd.NArg() == 1 { - // FIXME rename this parameter, to not be confused with the filters flag - v.Set("filter", matchName) - } - if *all { - v.Set("all", "1") - } - - body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false)) - - if err != nil { - return err - } - - outs := engine.NewTable("Created", 0) - if _, err := outs.ReadListFrom(body); err != nil { - return err - } - - w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - if !*quiet { - fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") - } - - for _, out := range outs.Data { - for _, repotag := range out.GetList("RepoTags") { - - repo, tag := parsers.ParseRepositoryTag(repotag) - outID := out.Get("Id") - if !*noTrunc { - outID = utils.TruncateID(outID) - } - - if !*quiet { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize")))) - } else { - fmt.Fprintln(w, outID) - } - } - } - - if !*quiet { - w.Flush() - } - } - return nil -} - -// FIXME: --viz and --tree are deprecated. Remove them in a future version. -func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[string]*engine.Table, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)) { - length := images.Len() - if length > 1 { - for index, image := range images.Data { - if index+1 == length { - printNode(cli, noTrunc, image, prefix+"└─") - if subimages, exists := byParent[image.Get("Id")]; exists { - cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode) - } - } else { - printNode(cli, noTrunc, image, prefix+"\u251C─") - if subimages, exists := byParent[image.Get("Id")]; exists { - cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode) - } - } - } - } else { - for _, image := range images.Data { - printNode(cli, noTrunc, image, prefix+"└─") - if subimages, exists := byParent[image.Get("Id")]; exists { - cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode) - } - } - } -} - -// FIXME: --viz and --tree are deprecated. Remove them in a future version. -func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) { - var ( - imageID string - parentID string - ) - if noTrunc { - imageID = image.Get("Id") - parentID = image.Get("ParentId") - } else { - imageID = utils.TruncateID(image.Get("Id")) - parentID = utils.TruncateID(image.Get("ParentId")) - } - if parentID == "" { - fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID) - } else { - fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID) - } - if image.GetList("RepoTags")[0] != ":" { - fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", - imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n")) - } -} - -// FIXME: --viz and --tree are deprecated. Remove them in a future version. -func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) { - var imageID string - if noTrunc { - imageID = image.Get("Id") - } else { - imageID = utils.TruncateID(image.Get("Id")) - } - - fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize")))) - if image.GetList("RepoTags")[0] != ":" { - fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", ")) - } else { - fmt.Fprint(cli.out, "\n") - } -} - -func (cli *DockerCli) CmdPs(args ...string) error { - var ( - err error - - psFilterArgs = filters.Args{} - v = url.Values{} - - cmd = cli.Subcmd("ps", "", "List containers", true) - quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") - size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") - all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") - noTrunc = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") - nLatest = cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.") - since = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.") - before = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.") - last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") - flFilter = opts.NewListOpts(nil) - ) - cmd.Require(flag.Exact, 0) - - cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited= - containers with exit code of \nstatus=(restarting|running|paused|exited)") - - utils.ParseFlags(cmd, args, true) - if *last == -1 && *nLatest { - *last = 1 - } - - if *all { - v.Set("all", "1") - } - - if *last != -1 { - v.Set("limit", strconv.Itoa(*last)) - } - - if *since != "" { - v.Set("since", *since) - } - - if *before != "" { - v.Set("before", *before) - } - - if *size { - v.Set("size", "1") - } - - // Consolidate all filter flags, and sanity check them. - // They'll get processed in the daemon/server. - for _, f := range flFilter.GetAll() { - if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil { - return err - } - } - - if len(psFilterArgs) > 0 { - filterJson, err := filters.ToParam(psFilterArgs) - if err != nil { - return err - } - - v.Set("filters", filterJson) - } - - body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false)) - if err != nil { - return err - } - - outs := engine.NewTable("Created", 0) - if _, err := outs.ReadListFrom(body); err != nil { - return err - } - - w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - if !*quiet { - fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") - - if *size { - fmt.Fprintln(w, "\tSIZE") - } else { - fmt.Fprint(w, "\n") - } - } - - stripNamePrefix := func(ss []string) []string { - for i, s := range ss { - ss[i] = s[1:] - } - - return ss - } - - for _, out := range outs.Data { - outID := out.Get("Id") - - if !*noTrunc { - outID = utils.TruncateID(outID) - } - - if *quiet { - fmt.Fprintln(w, outID) - - continue - } - - var ( - outNames = stripNamePrefix(out.GetList("Names")) - outCommand = strconv.Quote(out.Get("Command")) - ports = engine.NewTable("", 0) - ) - - if !*noTrunc { - outCommand = utils.Trunc(outCommand, 20) - - // only display the default name for the container with notrunc is passed - for _, name := range outNames { - if len(strings.Split(name, "/")) == 1 { - outNames = []string{name} - - break - } - } - } - - ports.ReadListFrom([]byte(out.Get("Ports"))) - - image := out.Get("Image") - if image == "" { - image = "" - } - - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand, - units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), - out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ",")) - - if *size { - if out.GetInt("SizeRootFs") > 0 { - fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs")))) - } else { - fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw")))) - } - - continue - } - - fmt.Fprint(w, "\n") - } - - if !*quiet { - w.Flush() - } - - return nil -} - -func (cli *DockerCli) CmdCommit(args ...string) error { - cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true) - flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit") - flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") - flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith \")") - // FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands. - flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands") - cmd.Require(flag.Max, 2) - cmd.Require(flag.Min, 1) - utils.ParseFlags(cmd, args, true) - - var ( - name = cmd.Arg(0) - repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) - ) - - //Check if the given image name can be resolved - if repository != "" { - if err := registry.ValidateRepositoryName(repository); err != nil { - return err - } - } - - v := url.Values{} - v.Set("container", name) - v.Set("repo", repository) - v.Set("tag", tag) - v.Set("comment", *flComment) - v.Set("author", *flAuthor) - - if *flPause != true { - v.Set("pause", "0") - } - - var ( - config *runconfig.Config - env engine.Env - ) - if *flConfig != "" { - config = &runconfig.Config{} - if err := json.Unmarshal([]byte(*flConfig), config); err != nil { - return err - } - } - stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false) - if err != nil { - return err - } - if err := env.Decode(stream); err != nil { - return err - } - - fmt.Fprintf(cli.out, "%s\n", env.Get("Id")) - return nil -} - -func (cli *DockerCli) CmdEvents(args ...string) error { - cmd := cli.Subcmd("events", "", "Get real time events from the server", true) - since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") - until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") - flFilter := opts.NewListOpts(nil) - cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e., 'event=stop')") - cmd.Require(flag.Exact, 0) - - utils.ParseFlags(cmd, args, true) - - var ( - v = url.Values{} - loc = time.FixedZone(time.Now().Zone()) - eventFilterArgs = filters.Args{} - ) - - // Consolidate all filter flags, and sanity check them early. - // They'll get process in the daemon/server. - for _, f := range flFilter.GetAll() { - var err error - eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs) - if err != nil { - return err - } - } - var setTime = func(key, value string) { - format := timeutils.RFC3339NanoFixed - if len(value) < len(format) { - format = format[:len(value)] - } - if t, err := time.ParseInLocation(format, value, loc); err == nil { - v.Set(key, strconv.FormatInt(t.Unix(), 10)) - } else { - v.Set(key, value) - } - } - if *since != "" { - setTime("since", *since) - } - if *until != "" { - setTime("until", *until) - } - if len(eventFilterArgs) > 0 { - filterJson, err := filters.ToParam(eventFilterArgs) - if err != nil { - return err - } - v.Set("filters", filterJson) - } - if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil { - return err - } - return nil -} - -func (cli *DockerCli) CmdExport(args ...string) error { - cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT", true) - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil { - return err - } - return nil -} - -func (cli *DockerCli) CmdDiff(args ...string) error { - cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true) - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false)) - - if err != nil { - return err - } - - outs := engine.NewTable("", 0) - if _, err := outs.ReadListFrom(body); err != nil { - return err - } - for _, change := range outs.Data { - var kind string - switch change.GetInt("Kind") { - case archive.ChangeModify: - kind = "C" - case archive.ChangeAdd: - kind = "A" - case archive.ChangeDelete: - kind = "D" - } - fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path")) - } - return nil -} - -func (cli *DockerCli) CmdLogs(args ...string) error { - var ( - cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true) - follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") - times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps") - tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)") - ) - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - name := cmd.Arg(0) - - stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) - if err != nil { - return err - } - - env := engine.Env{} - if err := env.Decode(stream); err != nil { - return err - } - - v := url.Values{} - v.Set("stdout", "1") - v.Set("stderr", "1") - - if *times { - v.Set("timestamps", "1") - } - - if *follow { - v.Set("follow", "1") - } - v.Set("tail", *tail) - - return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil) -} - -func (cli *DockerCli) CmdAttach(args ...string) error { - var ( - cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true) - noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN") - proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.") - ) - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - name := cmd.Arg(0) - - stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) - if err != nil { - return err - } - - env := engine.Env{} - if err := env.Decode(stream); err != nil { - return err - } - - if !env.GetSubEnv("State").GetBool("Running") { - return fmt.Errorf("You cannot attach to a stopped container, start it first") - } - - var ( - config = env.GetSubEnv("Config") - tty = config.GetBool("Tty") - ) - - if err := cli.CheckTtyInput(!*noStdin, tty); err != nil { - return err - } - - if tty && cli.isTerminalOut { - if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { - log.Debugf("Error monitoring TTY size: %s", err) - } - } - - var in io.ReadCloser - - v := url.Values{} - v.Set("stream", "1") - if !*noStdin && config.GetBool("OpenStdin") { - v.Set("stdin", "1") - in = cli.in - } - - v.Set("stdout", "1") - v.Set("stderr", "1") - - if *proxy && !tty { - sigc := cli.forwardAllSignals(cmd.Arg(0)) - defer signal.StopCatch(sigc) - } - - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil { - return err - } - - _, status, err := getExitCode(cli, cmd.Arg(0)) - if err != nil { - return err - } - if status != 0 { - return &utils.StatusError{StatusCode: status} - } - - return nil -} - -func (cli *DockerCli) CmdSearch(args ...string) error { - cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true) - noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") - trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds") - automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds") - stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars") - cmd.Require(flag.Exact, 1) - - utils.ParseFlags(cmd, args, true) - - v := url.Values{} - v.Set("term", cmd.Arg(0)) - - body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true)) - - if err != nil { - return err - } - outs := engine.NewTable("star_count", 0) - if _, err := outs.ReadListFrom(body); err != nil { - return err - } - w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) - fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n") - for _, out := range outs.Data { - if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) { - continue - } - desc := strings.Replace(out.Get("description"), "\n", " ", -1) - desc = strings.Replace(desc, "\r", " ", -1) - if !*noTrunc && len(desc) > 45 { - desc = utils.Trunc(desc, 42) + "..." - } - fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count")) - if out.GetBool("is_official") { - fmt.Fprint(w, "[OK]") - - } - fmt.Fprint(w, "\t") - if out.GetBool("is_automated") || out.GetBool("is_trusted") { - fmt.Fprint(w, "[OK]") - } - fmt.Fprint(w, "\n") - } - w.Flush() - return nil -} - -// Ports type - Used to parse multiple -p flags -type ports []int - -func (cli *DockerCli) CmdTag(args ...string) error { - cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true) - force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") - cmd.Require(flag.Exact, 2) - - utils.ParseFlags(cmd, args, true) - - var ( - repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) - v = url.Values{} - ) - - //Check if the given image name can be resolved - if err := registry.ValidateRepositoryName(repository); err != nil { - return err - } - v.Set("repo", repository) - v.Set("tag", tag) - - if *force { - v.Set("force", "1") - } - - if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil { - return err - } - return nil -} - -func (cli *DockerCli) pullImage(image string) error { - return cli.pullImageCustomOut(image, cli.out) -} - -func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { - v := url.Values{} - repos, tag := parsers.ParseRepositoryTag(image) - // pull only the image tagged 'latest' if no tag was specified - if tag == "" { - tag = graph.DEFAULTTAG - } - v.Set("fromImage", repos) - v.Set("tag", tag) - - // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, err := registry.ParseRepositoryInfo(repos) - if err != nil { - return err - } - - // Load the auth config file, to be able to pull the image - cli.LoadConfigFile() - - // Resolve the Auth config relevant for this server - authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) - buf, err := json.Marshal(authConfig) - if err != nil { - return err - } - - registryAuthHeader := []string{ - base64.URLEncoding.EncodeToString(buf), - } - if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { - return err - } - return nil -} - -type cidFile struct { - path string - file *os.File - written bool -} - -func newCIDFile(path string) (*cidFile, error) { - if _, err := os.Stat(path); err == nil { - return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path) - } - - f, err := os.Create(path) - if err != nil { - return nil, fmt.Errorf("Failed to create the container ID file: %s", err) - } - - return &cidFile{path: path, file: f}, nil -} - -func (cid *cidFile) Close() error { - cid.file.Close() - - if !cid.written { - if err := os.Remove(cid.path); err != nil { - return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) - } - } - - return nil -} - -func (cid *cidFile) Write(id string) error { - if _, err := cid.file.Write([]byte(id)); err != nil { - return fmt.Errorf("Failed to write the container ID to the file: %s", err) - } - cid.written = true - return nil -} - -func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) { - containerValues := url.Values{} - if name != "" { - containerValues.Set("name", name) - } - - mergedConfig := runconfig.MergeConfigs(config, hostConfig) - - var containerIDFile *cidFile - if cidfile != "" { - var err error - if containerIDFile, err = newCIDFile(cidfile); err != nil { - return nil, err - } - defer containerIDFile.Close() - } - - //create the container - stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false) - //if image not found try to pull it - if statusCode == 404 { - repo, tag := parsers.ParseRepositoryTag(config.Image) - if tag == "" { - tag = graph.DEFAULTTAG - } - fmt.Fprintf(cli.err, "Unable to find image '%s:%s' locally\n", repo, tag) - - // we don't want to write to stdout anything apart from container.ID - if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { - return nil, err - } - // Retry - if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil { - return nil, err - } - } else if err != nil { - return nil, err - } - - var result engine.Env - if err := result.Decode(stream); err != nil { - return nil, err - } - - for _, warning := range result.GetList("Warnings") { - fmt.Fprintf(cli.err, "WARNING: %s\n", warning) - } - - if containerIDFile != nil { - if err = containerIDFile.Write(result.Get("Id")); err != nil { - return nil, err - } - } - - return result, nil - -} - -func (cli *DockerCli) CmdCreate(args ...string) error { - cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true) - - // These are flags not stored in Config/HostConfig - var ( - flName = cmd.String([]string{"-name"}, "", "Assign a name to the container") - ) - - config, hostConfig, cmd, err := runconfig.Parse(cmd, args) - if err != nil { - utils.ReportError(cmd, err.Error(), true) - } - if config.Image == "" { - cmd.Usage() - return nil - } - - createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) - if err != nil { - return err - } - - fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id")) - - return nil -} - -func (cli *DockerCli) CmdRun(args ...string) error { - // FIXME: just use runconfig.Parse already - cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true) - - // These are flags not stored in Config/HostConfig - var ( - flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") - flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run the container in the background and print the new container ID") - flSigProxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.") - flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container") - flAttach *opts.ListOpts - - ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d") - ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm") - ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d") - ) - - config, hostConfig, cmd, err := runconfig.Parse(cmd, args) - // just in case the Parse does not exit - if err != nil { - utils.ReportError(cmd, err.Error(), true) - } - if config.Image == "" { - cmd.Usage() - return nil - } - - if !*flDetach { - if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil { - return err - } - } else { - if fl := cmd.Lookup("attach"); fl != nil { - flAttach = fl.Value.(*opts.ListOpts) - if flAttach.Len() != 0 { - return ErrConflictAttachDetach - } - } - if *flAutoRemove { - return ErrConflictDetachAutoRemove - } - - config.AttachStdin = false - config.AttachStdout = false - config.AttachStderr = false - config.StdinOnce = false - } - - // Disable flSigProxy when in TTY mode - sigProxy := *flSigProxy - if config.Tty { - sigProxy = false - } - - runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) - if err != nil { - return err - } - - if sigProxy { - sigc := cli.forwardAllSignals(runResult.Get("Id")) - defer signal.StopCatch(sigc) - } - - var ( - waitDisplayId chan struct{} - errCh chan error - ) - - if !config.AttachStdout && !config.AttachStderr { - // Make this asynchronous to allow the client to write to stdin before having to read the ID - waitDisplayId = make(chan struct{}) - go func() { - defer close(waitDisplayId) - fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id")) - }() - } - - if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") { - return ErrConflictRestartPolicyAndAutoRemove - } - - // We need to instantiate the chan because the select needs it. It can - // be closed but can't be uninitialized. - hijacked := make(chan io.Closer) - - // Block the return until the chan gets closed - defer func() { - log.Debugf("End of CmdRun(), Waiting for hijack to finish.") - if _, ok := <-hijacked; ok { - log.Errorf("Hijack did not finish (chan still open)") - } - }() - - if config.AttachStdin || config.AttachStdout || config.AttachStderr { - var ( - out, stderr io.Writer - in io.ReadCloser - v = url.Values{} - ) - v.Set("stream", "1") - - if config.AttachStdin { - v.Set("stdin", "1") - in = cli.in - } - if config.AttachStdout { - v.Set("stdout", "1") - out = cli.out - } - if config.AttachStderr { - v.Set("stderr", "1") - if config.Tty { - stderr = cli.out - } else { - stderr = cli.err - } - } - - errCh = promise.Go(func() error { - return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil) - }) - } else { - close(hijacked) - } - - // Acknowledge the hijack before starting - select { - case closer := <-hijacked: - // Make sure that the hijack gets closed when returning (results - // in closing the hijack chan and freeing server's goroutines) - if closer != nil { - defer closer.Close() - } - case err := <-errCh: - if err != nil { - log.Debugf("Error hijack: %s", err) - return err - } - } - - //start the container - if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", nil, false)); err != nil { - return err - } - - if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { - if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil { - log.Errorf("Error monitoring TTY size: %s", err) - } - } - - if errCh != nil { - if err := <-errCh; err != nil { - log.Debugf("Error hijack: %s", err) - return err - } - } - - // Detached mode: wait for the id to be displayed and return. - if !config.AttachStdout && !config.AttachStderr { - // Detached mode - <-waitDisplayId - return nil - } - - var status int - - // Attached mode - if *flAutoRemove { - // Autoremove: wait for the container to finish, retrieve - // the exit code and remove the container - if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil { - return err - } - if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { - return err - } - if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil { - return err - } - } else { - // No Autoremove: Simply retrieve the exit code - if !config.Tty { - // In non-TTY mode, we can't detach, so we must wait for container exit - if status, err = waitForExit(cli, runResult.Get("Id")); err != nil { - return err - } - } else { - // In TTY mode, there is a race: if the process dies too slowly, the state could - // be updated after the getExitCode call and result in the wrong exit code being reported - if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { - return err - } - } - } - if status != 0 { - return &utils.StatusError{StatusCode: status} - } - return nil -} - -func (cli *DockerCli) CmdCp(args ...string) error { - cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH", true) - cmd.Require(flag.Exact, 2) - - utils.ParseFlags(cmd, args, true) - - var copyData engine.Env - info := strings.Split(cmd.Arg(0), ":") - - if len(info) != 2 { - return fmt.Errorf("Error: Path not specified") - } - - copyData.Set("Resource", info[1]) - copyData.Set("HostPath", cmd.Arg(1)) - - stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false) - if stream != nil { - defer stream.Close() - } - if statusCode == 404 { - return fmt.Errorf("No such container: %v", info[0]) - } - if err != nil { - return err - } - - if statusCode == 200 { - if err := archive.Untar(stream, copyData.Get("HostPath"), &archive.TarOptions{NoLchown: true}); err != nil { - return err - } - } - return nil -} - -func (cli *DockerCli) CmdSave(args ...string) error { - cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true) - outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT") - cmd.Require(flag.Min, 1) - - utils.ParseFlags(cmd, args, true) - - var ( - output io.Writer = cli.out - err error - ) - if *outfile != "" { - output, err = os.Create(*outfile) - if err != nil { - return err - } - } else if cli.isTerminalOut { - return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") - } - - if len(cmd.Args()) == 1 { - image := cmd.Arg(0) - if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil { - return err - } - } else { - v := url.Values{} - for _, arg := range cmd.Args() { - v.Add("names", arg) - } - if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil { - return err - } - } - return nil -} - -func (cli *DockerCli) CmdLoad(args ...string) error { - cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true) - infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") - cmd.Require(flag.Exact, 0) - - utils.ParseFlags(cmd, args, true) - - var ( - input io.Reader = cli.in - err error - ) - if *infile != "" { - input, err = os.Open(*infile) - if err != nil { - return err - } - } - if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil { - return err - } - return nil -} - -func (cli *DockerCli) CmdExec(args ...string) error { - cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true) - - execConfig, err := runconfig.ParseExec(cmd, args) - // just in case the ParseExec does not exit - if execConfig.Container == "" || err != nil { - return &utils.StatusError{StatusCode: 1} - } - - stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false) - if err != nil { - return err - } - - var execResult engine.Env - if err := execResult.Decode(stream); err != nil { - return err - } - - execID := execResult.Get("Id") - - if execID == "" { - fmt.Fprintf(cli.out, "exec ID empty") - return nil - } - - if !execConfig.Detach { - if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { - return err - } - } else { - if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil { - return err - } - // For now don't print this - wait for when we support exec wait() - // fmt.Fprintf(cli.out, "%s\n", execID) - return nil - } - - // Interactive exec requested. - var ( - out, stderr io.Writer - in io.ReadCloser - hijacked = make(chan io.Closer) - errCh chan error - ) - - // Block the return until the chan gets closed - defer func() { - log.Debugf("End of CmdExec(), Waiting for hijack to finish.") - if _, ok := <-hijacked; ok { - log.Errorf("Hijack did not finish (chan still open)") - } - }() - - if execConfig.AttachStdin { - in = cli.in - } - if execConfig.AttachStdout { - out = cli.out - } - if execConfig.AttachStderr { - if execConfig.Tty { - stderr = cli.out - } else { - stderr = cli.err - } - } - errCh = promise.Go(func() error { - return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig) - }) - - // Acknowledge the hijack before starting - select { - case closer := <-hijacked: - // Make sure that hijack gets closed when returning. (result - // in closing hijack chan and freeing server's goroutines. - if closer != nil { - defer closer.Close() - } - case err := <-errCh: - if err != nil { - log.Debugf("Error hijack: %s", err) - return err - } - } - - if execConfig.Tty && cli.isTerminalIn { - if err := cli.monitorTtySize(execID, true); err != nil { - log.Errorf("Error monitoring TTY size: %s", err) - } - } - - if err := <-errCh; err != nil { - log.Debugf("Error hijack: %s", err) - return err - } - - var status int - if _, status, err = getExecExitCode(cli, execID); err != nil { - return err - } - - if status != 0 { - return &utils.StatusError{StatusCode: status} - } - - return nil -} - -type containerStats struct { - Name string - CpuPercentage float64 - Memory float64 - MemoryLimit float64 - MemoryPercentage float64 - NetworkRx float64 - NetworkTx float64 - mu sync.RWMutex - err error -} - -func (s *containerStats) Collect(cli *DockerCli) { - stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false) - if err != nil { - s.err = err - return - } - defer stream.Close() - var ( - previousCpu uint64 - previousSystem uint64 - start = true - dec = json.NewDecoder(stream) - u = make(chan error, 1) - ) - go func() { - for { - var v *stats.Stats - if err := dec.Decode(&v); err != nil { - u <- err - return - } - var ( - memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0 - cpuPercent = 0.0 - ) - if !start { - cpuPercent = calcuateCpuPercent(previousCpu, previousSystem, v) - } - start = false - s.mu.Lock() - s.CpuPercentage = cpuPercent - s.Memory = float64(v.MemoryStats.Usage) - s.MemoryLimit = float64(v.MemoryStats.Limit) - s.MemoryPercentage = memPercent - s.NetworkRx = float64(v.Network.RxBytes) - s.NetworkTx = float64(v.Network.TxBytes) - s.mu.Unlock() - previousCpu = v.CpuStats.CpuUsage.TotalUsage - previousSystem = v.CpuStats.SystemUsage - u <- nil - } - }() - for { - select { - case <-time.After(2 * time.Second): - // zero out the values if we have not received an update within - // the specified duration. - s.mu.Lock() - s.CpuPercentage = 0 - s.Memory = 0 - s.MemoryPercentage = 0 - s.mu.Unlock() - case err := <-u: - if err != nil { - s.mu.Lock() - s.err = err - s.mu.Unlock() - return - } - } - } -} - -func (s *containerStats) Display(w io.Writer) error { - s.mu.RLock() - defer s.mu.RUnlock() - if s.err != nil { - return s.err - } - fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n", - s.Name, - s.CpuPercentage, - units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit), - s.MemoryPercentage, - units.BytesSize(s.NetworkRx), units.BytesSize(s.NetworkTx)) - return nil -} - -func (cli *DockerCli) CmdStats(args ...string) error { - cmd := cli.Subcmd("stats", "CONTAINER", "Display a live stream of one or more containers' resource usage statistics", true) - cmd.Require(flag.Min, 1) - utils.ParseFlags(cmd, args, true) - - names := cmd.Args() - sort.Strings(names) - var ( - cStats []*containerStats - w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - ) - printHeader := func() { - fmt.Fprint(cli.out, "\033[2J") - fmt.Fprint(cli.out, "\033[H") - fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O") - } - for _, n := range names { - s := &containerStats{Name: n} - cStats = append(cStats, s) - go s.Collect(cli) - } - // do a quick pause so that any failed connections for containers that do not exist are able to be - // evicted before we display the initial or default values. - time.Sleep(500 * time.Millisecond) - var errs []string - for _, c := range cStats { - c.mu.Lock() - if c.err != nil { - errs = append(errs, fmt.Sprintf("%s: %s", c.Name, c.err.Error())) - } - c.mu.Unlock() - } - if len(errs) > 0 { - return fmt.Errorf("%s", strings.Join(errs, ", ")) - } - for _ = range time.Tick(500 * time.Millisecond) { - printHeader() - toRemove := []int{} - for i, s := range cStats { - if err := s.Display(w); err != nil { - toRemove = append(toRemove, i) - } - } - for j := len(toRemove) - 1; j >= 0; j-- { - i := toRemove[j] - cStats = append(cStats[:i], cStats[i+1:]...) - } - if len(cStats) == 0 { - return nil - } - w.Flush() - } - return nil -} - -func calcuateCpuPercent(previousCpu, previousSystem uint64, v *stats.Stats) float64 { - var ( - cpuPercent = 0.0 - // calculate the change for the cpu usage of the container in between readings - cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCpu) - // calculate the change for the entire system between readings - systemDelta = float64(v.CpuStats.SystemUsage - previousSystem) - ) - - if systemDelta > 0.0 && cpuDelta > 0.0 { - cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0 - } - return cpuPercent -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/client/hijack.go b/Godeps/_workspace/src/github.com/docker/docker/api/client/hijack.go deleted file mode 100644 index bb902405c0..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/client/hijack.go +++ /dev/null @@ -1,250 +0,0 @@ -package client - -import ( - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/http/httputil" - "os" - "runtime" - "strings" - "time" - - log "github.com/Sirupsen/logrus" - "github.com/docker/docker/api" - "github.com/docker/docker/dockerversion" - "github.com/docker/docker/pkg/promise" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/term" -) - -type tlsClientCon struct { - *tls.Conn - rawConn net.Conn -} - -func (c *tlsClientCon) CloseWrite() error { - // Go standard tls.Conn doesn't provide the CloseWrite() method so we do it - // on its underlying connection. - if cwc, ok := c.rawConn.(interface { - CloseWrite() error - }); ok { - return cwc.CloseWrite() - } - return nil -} - -func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) { - return tlsDialWithDialer(new(net.Dialer), network, addr, config) -} - -// We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in -// order to return our custom tlsClientCon struct which holds both the tls.Conn -// object _and_ its underlying raw connection. The rationale for this is that -// we need to be able to close the write end of the connection when attaching, -// which tls.Conn does not provide. -func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) { - // We want the Timeout and Deadline values from dialer to cover the - // whole process: TCP connection and TLS handshake. This means that we - // also need to start our own timers now. - timeout := dialer.Timeout - - if !dialer.Deadline.IsZero() { - deadlineTimeout := dialer.Deadline.Sub(time.Now()) - if timeout == 0 || deadlineTimeout < timeout { - timeout = deadlineTimeout - } - } - - var errChannel chan error - - if timeout != 0 { - errChannel = make(chan error, 2) - time.AfterFunc(timeout, func() { - errChannel <- errors.New("") - }) - } - - rawConn, err := dialer.Dial(network, addr) - if err != nil { - return nil, err - } - // When we set up a TCP connection for hijack, there could be long periods - // of inactivity (a long running command with no output) that in certain - // network setups may cause ECONNTIMEOUT, leaving the client in an unknown - // state. Setting TCP KeepAlive on the socket connection will prohibit - // ECONNTIMEOUT unless the socket connection truly is broken - if tcpConn, ok := rawConn.(*net.TCPConn); ok { - tcpConn.SetKeepAlive(true) - tcpConn.SetKeepAlivePeriod(30 * time.Second) - } - - colonPos := strings.LastIndex(addr, ":") - if colonPos == -1 { - colonPos = len(addr) - } - hostname := addr[:colonPos] - - // If no ServerName is set, infer the ServerName - // from the hostname we're connecting to. - if config.ServerName == "" { - // Make a copy to avoid polluting argument or default. - c := *config - c.ServerName = hostname - config = &c - } - - conn := tls.Client(rawConn, config) - - if timeout == 0 { - err = conn.Handshake() - } else { - go func() { - errChannel <- conn.Handshake() - }() - - err = <-errChannel - } - - if err != nil { - rawConn.Close() - return nil, err - } - - // This is Docker difference with standard's crypto/tls package: returned a - // wrapper which holds both the TLS and raw connections. - return &tlsClientCon{conn, rawConn}, nil -} - -func (cli *DockerCli) dial() (net.Conn, error) { - if cli.tlsConfig != nil && cli.proto != "unix" { - // Notice this isn't Go standard's tls.Dial function - return tlsDial(cli.proto, cli.addr, cli.tlsConfig) - } - return net.Dial(cli.proto, cli.addr) -} - -func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}) error { - defer func() { - if started != nil { - close(started) - } - }() - - params, err := cli.encodeData(data) - if err != nil { - return err - } - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) - if err != nil { - return err - } - req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) - req.Header.Set("Content-Type", "text/plain") - req.Header.Set("Connection", "Upgrade") - req.Header.Set("Upgrade", "tcp") - req.Host = cli.addr - - dial, err := cli.dial() - // When we set up a TCP connection for hijack, there could be long periods - // of inactivity (a long running command with no output) that in certain - // network setups may cause ECONNTIMEOUT, leaving the client in an unknown - // state. Setting TCP KeepAlive on the socket connection will prohibit - // ECONNTIMEOUT unless the socket connection truly is broken - if tcpConn, ok := dial.(*net.TCPConn); ok { - tcpConn.SetKeepAlive(true) - tcpConn.SetKeepAlivePeriod(30 * time.Second) - } - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") - } - return err - } - clientconn := httputil.NewClientConn(dial, nil) - defer clientconn.Close() - - // Server hijacks the connection, error 'connection closed' expected - clientconn.Do(req) - - rwc, br := clientconn.Hijack() - defer rwc.Close() - - if started != nil { - started <- rwc - } - - var receiveStdout chan error - - var oldState *term.State - - if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" { - oldState, err = term.SetRawTerminal(cli.inFd) - if err != nil { - return err - } - defer term.RestoreTerminal(cli.inFd, oldState) - } - - if stdout != nil || stderr != nil { - receiveStdout = promise.Go(func() (err error) { - defer func() { - if in != nil { - if setRawTerminal && cli.isTerminalIn { - term.RestoreTerminal(cli.inFd, oldState) - } - // For some reason this Close call blocks on darwin.. - // As the client exists right after, simply discard the close - // until we find a better solution. - if runtime.GOOS != "darwin" { - in.Close() - } - } - }() - - // When TTY is ON, use regular copy - if setRawTerminal && stdout != nil { - _, err = io.Copy(stdout, br) - } else { - _, err = stdcopy.StdCopy(stdout, stderr, br) - } - log.Debugf("[hijack] End of stdout") - return err - }) - } - - sendStdin := promise.Go(func() error { - if in != nil { - io.Copy(rwc, in) - log.Debugf("[hijack] End of stdin") - } - - if conn, ok := rwc.(interface { - CloseWrite() error - }); ok { - if err := conn.CloseWrite(); err != nil { - log.Debugf("Couldn't send EOF: %s", err) - } - } - // Discard errors due to pipe interruption - return nil - }) - - if stdout != nil || stderr != nil { - if err := <-receiveStdout; err != nil { - log.Debugf("Error receiveStdout: %s", err) - return err - } - } - - if !cli.isTerminalIn { - if err := <-sendStdin; err != nil { - log.Debugf("Error sendStdin: %s", err) - return err - } - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/client/utils.go b/Godeps/_workspace/src/github.com/docker/docker/api/client/utils.go deleted file mode 100644 index 86e221ebf4..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/client/utils.go +++ /dev/null @@ -1,296 +0,0 @@ -package client - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - gosignal "os/signal" - "strconv" - "strings" - - log "github.com/Sirupsen/logrus" - "github.com/docker/docker/api" - "github.com/docker/docker/dockerversion" - "github.com/docker/docker/engine" - "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/term" - "github.com/docker/docker/registry" - "github.com/docker/docker/utils" -) - -var ( - ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") -) - -func (cli *DockerCli) HTTPClient() *http.Client { - return &http.Client{Transport: cli.transport} -} - -func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) { - params := bytes.NewBuffer(nil) - if data != nil { - if env, ok := data.(engine.Env); ok { - if err := env.Encode(params); err != nil { - return nil, err - } - } else { - buf, err := json.Marshal(data) - if err != nil { - return nil, err - } - if _, err := params.Write(buf); err != nil { - return nil, err - } - } - } - return params, nil -} - -func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) { - params, err := cli.encodeData(data) - if err != nil { - return nil, -1, err - } - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) - if err != nil { - return nil, -1, err - } - if passAuthInfo { - cli.LoadConfigFile() - // Resolve the Auth config relevant for this server - authConfig := cli.configFile.Configs[registry.IndexServerAddress()] - getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) { - buf, err := json.Marshal(authConfig) - if err != nil { - return nil, err - } - registryAuthHeader := []string{ - base64.URLEncoding.EncodeToString(buf), - } - return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil - } - if headers, err := getHeaders(authConfig); err == nil && headers != nil { - for k, v := range headers { - req.Header[k] = v - } - } - } - req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) - req.URL.Host = cli.addr - req.URL.Scheme = cli.scheme - if data != nil { - req.Header.Set("Content-Type", "application/json") - } else if method == "POST" { - req.Header.Set("Content-Type", "text/plain") - } - resp, err := cli.HTTPClient().Do(req) - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return nil, -1, ErrConnectionRefused - } - - if cli.tlsConfig == nil { - return nil, -1, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err) - } - return nil, -1, fmt.Errorf("An error occurred trying to connect: %v", err) - - } - - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, -1, err - } - if len(body) == 0 { - return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL) - } - return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body)) - } - - return resp.Body, resp.StatusCode, nil -} - -func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error { - return cli.streamHelper(method, path, true, in, out, nil, headers) -} - -func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error { - if (method == "POST" || method == "PUT") && in == nil { - in = bytes.NewReader([]byte{}) - } - - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in) - if err != nil { - return err - } - req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) - req.URL.Host = cli.addr - req.URL.Scheme = cli.scheme - if method == "POST" { - req.Header.Set("Content-Type", "text/plain") - } - - if headers != nil { - for k, v := range headers { - req.Header[k] = v - } - } - resp, err := cli.HTTPClient().Do(req) - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") - } - return err - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - if len(body) == 0 { - return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode)) - } - return fmt.Errorf("Error: %s", bytes.TrimSpace(body)) - } - - if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") { - return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut) - } - if stdout != nil || stderr != nil { - // When TTY is ON, use regular copy - if setRawTerminal { - _, err = io.Copy(stdout, resp.Body) - } else { - _, err = stdcopy.StdCopy(stdout, stderr, resp.Body) - } - log.Debugf("[stream] End of stdout") - return err - } - return nil -} - -func (cli *DockerCli) resizeTty(id string, isExec bool) { - height, width := cli.getTtySize() - if height == 0 && width == 0 { - return - } - v := url.Values{} - v.Set("h", strconv.Itoa(height)) - v.Set("w", strconv.Itoa(width)) - - path := "" - if !isExec { - path = "/containers/" + id + "/resize?" - } else { - path = "/exec/" + id + "/resize?" - } - - if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, false)); err != nil { - log.Debugf("Error resize: %s", err) - } -} - -func waitForExit(cli *DockerCli, containerId string) (int, error) { - stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false) - if err != nil { - return -1, err - } - - var out engine.Env - if err := out.Decode(stream); err != nil { - return -1, err - } - return out.GetInt("StatusCode"), nil -} - -// getExitCode perform an inspect on the container. It returns -// the running state and the exit code. -func getExitCode(cli *DockerCli, containerId string) (bool, int, error) { - stream, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil, false) - if err != nil { - // If we can't connect, then the daemon probably died. - if err != ErrConnectionRefused { - return false, -1, err - } - return false, -1, nil - } - - var result engine.Env - if err := result.Decode(stream); err != nil { - return false, -1, err - } - - state := result.GetSubEnv("State") - return state.GetBool("Running"), state.GetInt("ExitCode"), nil -} - -// getExecExitCode perform an inspect on the exec command. It returns -// the running state and the exit code. -func getExecExitCode(cli *DockerCli, execId string) (bool, int, error) { - stream, _, err := cli.call("GET", "/exec/"+execId+"/json", nil, false) - if err != nil { - // If we can't connect, then the daemon probably died. - if err != ErrConnectionRefused { - return false, -1, err - } - return false, -1, nil - } - - var result engine.Env - if err := result.Decode(stream); err != nil { - return false, -1, err - } - - return result.GetBool("Running"), result.GetInt("ExitCode"), nil -} - -func (cli *DockerCli) monitorTtySize(id string, isExec bool) error { - cli.resizeTty(id, isExec) - - sigchan := make(chan os.Signal, 1) - gosignal.Notify(sigchan, signal.SIGWINCH) - go func() { - for _ = range sigchan { - cli.resizeTty(id, isExec) - } - }() - return nil -} - -func (cli *DockerCli) getTtySize() (int, int) { - if !cli.isTerminalOut { - return 0, 0 - } - ws, err := term.GetWinsize(cli.outFd) - if err != nil { - log.Debugf("Error getting size: %s", err) - if ws == nil { - return 0, 0 - } - } - return int(ws.Height), int(ws.Width) -} - -func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) { - if stream != nil { - defer stream.Close() - } - if err != nil { - return nil, statusCode, err - } - body, err := ioutil.ReadAll(stream) - if err != nil { - return nil, -1, err - } - return body, statusCode, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/common.go b/Godeps/_workspace/src/github.com/docker/docker/api/common.go deleted file mode 100644 index 1bbb6d3937..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/common.go +++ /dev/null @@ -1,75 +0,0 @@ -package api - -import ( - "fmt" - "mime" - "os" - "path/filepath" - "strings" - - log "github.com/Sirupsen/logrus" - "github.com/docker/docker/engine" - "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/version" - "github.com/docker/libtrust" -) - -const ( - APIVERSION version.Version = "1.17" - DEFAULTHTTPHOST = "127.0.0.1" - DEFAULTUNIXSOCKET = "/var/run/docker.sock" - DefaultDockerfileName string = "Dockerfile" -) - -func ValidateHost(val string) (string, error) { - host, err := parsers.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val) - if err != nil { - return val, err - } - return host, nil -} - -//TODO remove, used on < 1.5 in getContainersJSON -func DisplayablePorts(ports *engine.Table) string { - result := []string{} - ports.SetKey("PublicPort") - ports.Sort() - for _, port := range ports.Data { - if port.Get("IP") == "" { - result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PrivatePort"), port.Get("Type"))) - } else { - result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type"))) - } - } - return strings.Join(result, ", ") -} - -func MatchesContentType(contentType, expectedType string) bool { - mimetype, _, err := mime.ParseMediaType(contentType) - if err != nil { - log.Errorf("Error parsing media type: %s error: %s", contentType, err.Error()) - } - return err == nil && mimetype == expectedType -} - -// LoadOrCreateTrustKey attempts to load the libtrust key at the given path, -// otherwise generates a new one -func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) { - err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700) - if err != nil { - return nil, err - } - trustKey, err := libtrust.LoadKeyFile(trustKeyPath) - if err == libtrust.ErrKeyFileDoesNotExist { - trustKey, err = libtrust.GenerateECP256PrivateKey() - if err != nil { - return nil, fmt.Errorf("Error generating key: %s", err) - } - if err := libtrust.SaveKey(trustKeyPath, trustKey); err != nil { - return nil, fmt.Errorf("Error saving key file: %s", err) - } - } else if err != nil { - return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err) - } - return trustKey, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/server/MAINTAINERS b/Godeps/_workspace/src/github.com/docker/docker/api/server/MAINTAINERS deleted file mode 100644 index dee1eec042..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/server/MAINTAINERS +++ /dev/null @@ -1,2 +0,0 @@ -Victor Vieux (@vieux) -# Johan Euphrosine (@proppy) diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/server/server.go b/Godeps/_workspace/src/github.com/docker/docker/api/server/server.go deleted file mode 100644 index 9bb42f6c87..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/server/server.go +++ /dev/null @@ -1,1653 +0,0 @@ -package server - -import ( - "bufio" - "bytes" - - "encoding/base64" - "encoding/json" - "expvar" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/pprof" - "os" - "strconv" - "strings" - "syscall" - - "crypto/tls" - "crypto/x509" - - "code.google.com/p/go.net/websocket" - "github.com/docker/libcontainer/user" - "github.com/gorilla/mux" - - log "github.com/Sirupsen/logrus" - "github.com/docker/docker/api" - "github.com/docker/docker/daemon/networkdriver/portallocator" - "github.com/docker/docker/engine" - "github.com/docker/docker/pkg/listenbuffer" - "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/systemd" - "github.com/docker/docker/pkg/version" - "github.com/docker/docker/registry" - "github.com/docker/docker/utils" -) - -var ( - activationLock chan struct{} -) - -type HttpServer struct { - srv *http.Server - l net.Listener -} - -func (s *HttpServer) Serve() error { - return s.srv.Serve(s.l) -} -func (s *HttpServer) Close() error { - return s.l.Close() -} - -type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error - -func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { - conn, _, err := w.(http.Hijacker).Hijack() - if err != nil { - return nil, nil, err - } - // Flush the options to make sure the client sets the raw mode - conn.Write([]byte{}) - return conn, conn, nil -} - -func closeStreams(streams ...interface{}) { - for _, stream := range streams { - if tcpc, ok := stream.(interface { - CloseWrite() error - }); ok { - tcpc.CloseWrite() - } else if closer, ok := stream.(io.Closer); ok { - closer.Close() - } - } -} - -// Check to make sure request's Content-Type is application/json -func checkForJson(r *http.Request) error { - ct := r.Header.Get("Content-Type") - - // No Content-Type header is ok as long as there's no Body - if ct == "" { - if r.Body == nil || r.ContentLength == 0 { - return nil - } - } - - // Otherwise it better be json - if api.MatchesContentType(ct, "application/json") { - return nil - } - return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct) -} - -//If we don't do this, POST method without Content-type (even with empty body) will fail -func parseForm(r *http.Request) error { - if r == nil { - return nil - } - if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") { - return err - } - return nil -} - -func parseMultipartForm(r *http.Request) error { - if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") { - return err - } - return nil -} - -func httpError(w http.ResponseWriter, err error) { - statusCode := http.StatusInternalServerError - // FIXME: this is brittle and should not be necessary. - // If we need to differentiate between different possible error types, we should - // create appropriate error types with clearly defined meaning. - errStr := strings.ToLower(err.Error()) - if strings.Contains(errStr, "no such") { - statusCode = http.StatusNotFound - } else if strings.Contains(errStr, "bad parameter") { - statusCode = http.StatusBadRequest - } else if strings.Contains(errStr, "conflict") { - statusCode = http.StatusConflict - } else if strings.Contains(errStr, "impossible") { - statusCode = http.StatusNotAcceptable - } else if strings.Contains(errStr, "wrong login/password") { - statusCode = http.StatusUnauthorized - } else if strings.Contains(errStr, "hasn't been activated") { - statusCode = http.StatusForbidden - } - - if err != nil { - log.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error()) - http.Error(w, err.Error(), statusCode) - } -} - -func writeJSON(w http.ResponseWriter, code int, v engine.Env) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(code) - return v.Encode(w) -} - -func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) { - w.Header().Set("Content-Type", "application/json") - if flush { - job.Stdout.Add(utils.NewWriteFlusher(w)) - } else { - job.Stdout.Add(w) - } -} - -func getBoolParam(value string) (bool, error) { - if value == "" { - return false, nil - } - ret, err := strconv.ParseBool(value) - if err != nil { - return false, fmt.Errorf("Bad parameter") - } - return ret, nil -} - -func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - var ( - authConfig, err = ioutil.ReadAll(r.Body) - job = eng.Job("auth") - stdoutBuffer = bytes.NewBuffer(nil) - ) - if err != nil { - return err - } - job.Setenv("authConfig", string(authConfig)) - job.Stdout.Add(stdoutBuffer) - if err = job.Run(); err != nil { - return err - } - if status := engine.Tail(stdoutBuffer, 1); status != "" { - var env engine.Env - env.Set("Status", status) - return writeJSON(w, http.StatusOK, env) - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - w.Header().Set("Content-Type", "application/json") - eng.ServeHTTP(w, r) - return nil -} - -func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := parseForm(r); err != nil { - return err - } - job := eng.Job("kill", vars["name"]) - if sig := r.Form.Get("signal"); sig != "" { - job.Args = append(job.Args, sig) - } - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := parseForm(r); err != nil { - return err - } - job := eng.Job("pause", vars["name"]) - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := parseForm(r); err != nil { - return err - } - job := eng.Job("unpause", vars["name"]) - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - job := eng.Job("export", vars["name"]) - job.Stdout.Add(w) - if err := job.Run(); err != nil { - return err - } - return nil -} - -func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - - var ( - err error - outs *engine.Table - job = eng.Job("images") - ) - - job.Setenv("filters", r.Form.Get("filters")) - // FIXME this parameter could just be a match filter - job.Setenv("filter", r.Form.Get("filter")) - job.Setenv("all", r.Form.Get("all")) - - if version.GreaterThanOrEqualTo("1.7") { - streamJSON(job, w, false) - } else if outs, err = job.Stdout.AddListTable(); err != nil { - return err - } - - if err := job.Run(); err != nil { - return err - } - - if version.LessThan("1.7") && outs != nil { // Convert to legacy format - outsLegacy := engine.NewTable("Created", 0) - for _, out := range outs.Data { - for _, repoTag := range out.GetList("RepoTags") { - repo, tag := parsers.ParseRepositoryTag(repoTag) - outLegacy := &engine.Env{} - outLegacy.Set("Repository", repo) - outLegacy.SetJson("Tag", tag) - outLegacy.Set("Id", out.Get("Id")) - outLegacy.SetInt64("Created", out.GetInt64("Created")) - outLegacy.SetInt64("Size", out.GetInt64("Size")) - outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize")) - outsLegacy.Add(outLegacy) - } - } - w.Header().Set("Content-Type", "application/json") - if _, err := outsLegacy.WriteListTo(w); err != nil { - return err - } - } - return nil -} - -func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if version.GreaterThan("1.6") { - w.WriteHeader(http.StatusNotFound) - return fmt.Errorf("This is now implemented in the client.") - } - eng.ServeHTTP(w, r) - return nil -} - -func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - w.Header().Set("Content-Type", "application/json") - eng.ServeHTTP(w, r) - return nil -} - -func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - - var job = eng.Job("events") - streamJSON(job, w, true) - job.Setenv("since", r.Form.Get("since")) - job.Setenv("until", r.Form.Get("until")) - job.Setenv("filters", r.Form.Get("filters")) - return job.Run() -} - -func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - var job = eng.Job("history", vars["name"]) - streamJSON(job, w, false) - - if err := job.Run(); err != nil { - return err - } - return nil -} - -func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - var job = eng.Job("container_changes", vars["name"]) - streamJSON(job, w, false) - - return job.Run() -} - -func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if version.LessThan("1.4") { - return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.") - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := parseForm(r); err != nil { - return err - } - - job := eng.Job("top", vars["name"], r.Form.Get("ps_args")) - streamJSON(job, w, false) - return job.Run() -} - -func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - var ( - err error - outs *engine.Table - job = eng.Job("containers") - ) - - job.Setenv("all", r.Form.Get("all")) - job.Setenv("size", r.Form.Get("size")) - job.Setenv("since", r.Form.Get("since")) - job.Setenv("before", r.Form.Get("before")) - job.Setenv("limit", r.Form.Get("limit")) - job.Setenv("filters", r.Form.Get("filters")) - - if version.GreaterThanOrEqualTo("1.5") { - streamJSON(job, w, false) - } else if outs, err = job.Stdout.AddTable(); err != nil { - return err - } - if err = job.Run(); err != nil { - return err - } - if version.LessThan("1.5") { // Convert to legacy format - for _, out := range outs.Data { - ports := engine.NewTable("", 0) - ports.ReadListFrom([]byte(out.Get("Ports"))) - out.Set("Ports", api.DisplayablePorts(ports)) - } - w.Header().Set("Content-Type", "application/json") - if _, err = outs.WriteListTo(w); err != nil { - return err - } - } - return nil -} - -func getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - name := vars["name"] - job := eng.Job("container_stats", name) - streamJSON(job, w, true) - return job.Run() -} - -func getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - var ( - inspectJob = eng.Job("container_inspect", vars["name"]) - logsJob = eng.Job("logs", vars["name"]) - c, err = inspectJob.Stdout.AddEnv() - ) - if err != nil { - return err - } - logsJob.Setenv("follow", r.Form.Get("follow")) - logsJob.Setenv("tail", r.Form.Get("tail")) - logsJob.Setenv("stdout", r.Form.Get("stdout")) - logsJob.Setenv("stderr", r.Form.Get("stderr")) - logsJob.Setenv("timestamps", r.Form.Get("timestamps")) - // Validate args here, because we can't return not StatusOK after job.Run() call - stdout, stderr := logsJob.GetenvBool("stdout"), logsJob.GetenvBool("stderr") - if !(stdout || stderr) { - return fmt.Errorf("Bad parameters: you must choose at least one stream") - } - if err = inspectJob.Run(); err != nil { - return err - } - - var outStream, errStream io.Writer - outStream = utils.NewWriteFlusher(w) - - if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") { - errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) - outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) - } else { - errStream = outStream - } - - logsJob.Stdout.Add(outStream) - logsJob.Stderr.Set(errStream) - if err := logsJob.Run(); err != nil { - fmt.Fprintf(outStream, "Error running logs job: %s\n", err) - } - return nil -} - -func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag")) - job.Setenv("force", r.Form.Get("force")) - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusCreated) - return nil -} - -func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - var ( - config engine.Env - env engine.Env - job = eng.Job("commit", r.Form.Get("container")) - stdoutBuffer = bytes.NewBuffer(nil) - ) - - if err := checkForJson(r); err != nil { - return err - } - - if err := config.Decode(r.Body); err != nil { - log.Errorf("%s", err) - } - - if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { - job.Setenv("pause", "1") - } else { - job.Setenv("pause", r.FormValue("pause")) - } - - job.Setenv("repo", r.Form.Get("repo")) - job.Setenv("tag", r.Form.Get("tag")) - job.Setenv("author", r.Form.Get("author")) - job.Setenv("comment", r.Form.Get("comment")) - job.SetenvSubEnv("config", &config) - - job.Stdout.Add(stdoutBuffer) - if err := job.Run(); err != nil { - return err - } - env.Set("Id", engine.Tail(stdoutBuffer, 1)) - return writeJSON(w, http.StatusCreated, env) -} - -// Creates an image from Pull or from Import -func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - - var ( - image = r.Form.Get("fromImage") - repo = r.Form.Get("repo") - tag = r.Form.Get("tag") - job *engine.Job - ) - authEncoded := r.Header.Get("X-Registry-Auth") - authConfig := ®istry.AuthConfig{} - if authEncoded != "" { - authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) - if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { - // for a pull it is not an error if no auth was given - // to increase compatibility with the existing api it is defaulting to be empty - authConfig = ®istry.AuthConfig{} - } - } - if image != "" { //pull - if tag == "" { - image, tag = parsers.ParseRepositoryTag(image) - } - metaHeaders := map[string][]string{} - for k, v := range r.Header { - if strings.HasPrefix(k, "X-Meta-") { - metaHeaders[k] = v - } - } - job = eng.Job("pull", image, tag) - job.SetenvBool("parallel", version.GreaterThan("1.3")) - job.SetenvJson("metaHeaders", metaHeaders) - job.SetenvJson("authConfig", authConfig) - } else { //import - if tag == "" { - repo, tag = parsers.ParseRepositoryTag(repo) - } - job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag) - job.Stdin.Add(r.Body) - } - - if version.GreaterThan("1.0") { - job.SetenvBool("json", true) - streamJSON(job, w, true) - } else { - job.Stdout.Add(utils.NewWriteFlusher(w)) - } - if err := job.Run(); err != nil { - if !job.Stdout.Used() { - return err - } - sf := utils.NewStreamFormatter(version.GreaterThan("1.0")) - w.Write(sf.FormatError(err)) - } - - return nil -} - -func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - var ( - authEncoded = r.Header.Get("X-Registry-Auth") - authConfig = ®istry.AuthConfig{} - metaHeaders = map[string][]string{} - ) - - if authEncoded != "" { - authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) - if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { - // for a search it is not an error if no auth was given - // to increase compatibility with the existing api it is defaulting to be empty - authConfig = ®istry.AuthConfig{} - } - } - for k, v := range r.Header { - if strings.HasPrefix(k, "X-Meta-") { - metaHeaders[k] = v - } - } - - var job = eng.Job("search", r.Form.Get("term")) - job.SetenvJson("metaHeaders", metaHeaders) - job.SetenvJson("authConfig", authConfig) - streamJSON(job, w, false) - - return job.Run() -} - -func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - metaHeaders := map[string][]string{} - for k, v := range r.Header { - if strings.HasPrefix(k, "X-Meta-") { - metaHeaders[k] = v - } - } - if err := parseForm(r); err != nil { - return err - } - authConfig := ®istry.AuthConfig{} - - authEncoded := r.Header.Get("X-Registry-Auth") - if authEncoded != "" { - // the new format is to handle the authConfig as a header - authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) - if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { - // to increase compatibility to existing api it is defaulting to be empty - authConfig = ®istry.AuthConfig{} - } - } else { - // the old format is supported for compatibility if there was no authConfig header - if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { - return err - } - } - - job := eng.Job("push", vars["name"]) - job.SetenvJson("metaHeaders", metaHeaders) - job.SetenvJson("authConfig", authConfig) - job.Setenv("tag", r.Form.Get("tag")) - if version.GreaterThan("1.0") { - job.SetenvBool("json", true) - streamJSON(job, w, true) - } else { - job.Stdout.Add(utils.NewWriteFlusher(w)) - } - - if err := job.Run(); err != nil { - if !job.Stdout.Used() { - return err - } - sf := utils.NewStreamFormatter(version.GreaterThan("1.0")) - w.Write(sf.FormatError(err)) - } - return nil -} - -func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := parseForm(r); err != nil { - return err - } - if version.GreaterThan("1.0") { - w.Header().Set("Content-Type", "application/x-tar") - } - var job *engine.Job - if name, ok := vars["name"]; ok { - job = eng.Job("image_export", name) - } else { - job = eng.Job("image_export", r.Form["names"]...) - } - job.Stdout.Add(w) - return job.Run() -} - -func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - job := eng.Job("load") - job.Stdin.Add(r.Body) - return job.Run() -} - -func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return nil - } - var ( - out engine.Env - job = eng.Job("create", r.Form.Get("name")) - outWarnings []string - stdoutBuffer = bytes.NewBuffer(nil) - warnings = bytes.NewBuffer(nil) - ) - - if err := checkForJson(r); err != nil { - return err - } - - if err := job.DecodeEnv(r.Body); err != nil { - return err - } - // Read container ID from the first line of stdout - job.Stdout.Add(stdoutBuffer) - // Read warnings from stderr - job.Stderr.Add(warnings) - if err := job.Run(); err != nil { - return err - } - // Parse warnings from stderr - scanner := bufio.NewScanner(warnings) - for scanner.Scan() { - outWarnings = append(outWarnings, scanner.Text()) - } - out.Set("Id", engine.Tail(stdoutBuffer, 1)) - out.SetList("Warnings", outWarnings) - - return writeJSON(w, http.StatusCreated, out) -} - -func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - job := eng.Job("restart", vars["name"]) - job.Setenv("t", r.Form.Get("t")) - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - newName := r.URL.Query().Get("name") - job := eng.Job("container_rename", vars["name"], newName) - job.Setenv("t", r.Form.Get("t")) - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - job := eng.Job("rm", vars["name"]) - - job.Setenv("forceRemove", r.Form.Get("force")) - - job.Setenv("removeVolume", r.Form.Get("v")) - job.Setenv("removeLink", r.Form.Get("link")) - if err := job.Run(); err != nil { - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - var job = eng.Job("image_delete", vars["name"]) - streamJSON(job, w, false) - job.Setenv("force", r.Form.Get("force")) - job.Setenv("noprune", r.Form.Get("noprune")) - - return job.Run() -} - -func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - var ( - name = vars["name"] - job = eng.Job("start", name) - ) - - // If contentLength is -1, we can assumed chunked encoding - // or more technically that the length is unknown - // http://golang.org/src/pkg/net/http/request.go#L139 - // net/http otherwise seems to swallow any headers related to chunked encoding - // including r.TransferEncoding - // allow a nil body for backwards compatibility - if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) { - if err := checkForJson(r); err != nil { - return err - } - - if err := job.DecodeEnv(r.Body); err != nil { - return err - } - } - - if err := job.Run(); err != nil { - if err.Error() == "Container already started" { - w.WriteHeader(http.StatusNotModified) - return nil - } - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - job := eng.Job("stop", vars["name"]) - job.Setenv("t", r.Form.Get("t")) - if err := job.Run(); err != nil { - if err.Error() == "Container already stopped" { - w.WriteHeader(http.StatusNotModified) - return nil - } - return err - } - w.WriteHeader(http.StatusNoContent) - return nil -} - -func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - var ( - env engine.Env - stdoutBuffer = bytes.NewBuffer(nil) - job = eng.Job("wait", vars["name"]) - ) - job.Stdout.Add(stdoutBuffer) - if err := job.Run(); err != nil { - return err - } - - env.Set("StatusCode", engine.Tail(stdoutBuffer, 1)) - return writeJSON(w, http.StatusOK, env) -} - -func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil { - return err - } - return nil -} - -func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - var ( - job = eng.Job("container_inspect", vars["name"]) - c, err = job.Stdout.AddEnv() - ) - if err != nil { - return err - } - if err = job.Run(); err != nil { - return err - } - - inStream, outStream, err := hijackServer(w) - if err != nil { - return err - } - defer closeStreams(inStream, outStream) - - var errStream io.Writer - - if _, ok := r.Header["Upgrade"]; ok { - fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") - } else { - fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") - } - - if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") { - errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) - outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) - } else { - errStream = outStream - } - - job = eng.Job("attach", vars["name"]) - job.Setenv("logs", r.Form.Get("logs")) - job.Setenv("stream", r.Form.Get("stream")) - job.Setenv("stdin", r.Form.Get("stdin")) - job.Setenv("stdout", r.Form.Get("stdout")) - job.Setenv("stderr", r.Form.Get("stderr")) - job.Stdin.Add(inStream) - job.Stdout.Add(outStream) - job.Stderr.Set(errStream) - if err := job.Run(); err != nil { - fmt.Fprintf(outStream, "Error attaching: %s\n", err) - - } - return nil -} - -func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - if err := eng.Job("container_inspect", vars["name"]).Run(); err != nil { - return err - } - - h := websocket.Handler(func(ws *websocket.Conn) { - defer ws.Close() - job := eng.Job("attach", vars["name"]) - job.Setenv("logs", r.Form.Get("logs")) - job.Setenv("stream", r.Form.Get("stream")) - job.Setenv("stdin", r.Form.Get("stdin")) - job.Setenv("stdout", r.Form.Get("stdout")) - job.Setenv("stderr", r.Form.Get("stderr")) - job.Stdin.Add(ws) - job.Stdout.Add(ws) - job.Stderr.Set(ws) - if err := job.Run(); err != nil { - log.Errorf("Error attaching websocket: %s", err) - } - }) - h.ServeHTTP(w, r) - - return nil -} - -func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - var job = eng.Job("container_inspect", vars["name"]) - if version.LessThan("1.12") { - job.SetenvBool("raw", true) - } - streamJSON(job, w, false) - return job.Run() -} - -func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter 'id'") - } - var job = eng.Job("execInspect", vars["id"]) - streamJSON(job, w, false) - return job.Run() -} - -func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - var job = eng.Job("image_inspect", vars["name"]) - if version.LessThan("1.12") { - job.SetenvBool("raw", true) - } - streamJSON(job, w, false) - return job.Run() -} - -func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if version.LessThan("1.3") { - return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") - } - var ( - authEncoded = r.Header.Get("X-Registry-Auth") - authConfig = ®istry.AuthConfig{} - configFileEncoded = r.Header.Get("X-Registry-Config") - configFile = ®istry.ConfigFile{} - job = eng.Job("build") - ) - - // This block can be removed when API versions prior to 1.9 are deprecated. - // Both headers will be parsed and sent along to the daemon, but if a non-empty - // ConfigFile is present, any value provided as an AuthConfig directly will - // be overridden. See BuildFile::CmdFrom for details. - if version.LessThan("1.9") && authEncoded != "" { - authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) - if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { - // for a pull it is not an error if no auth was given - // to increase compatibility with the existing api it is defaulting to be empty - authConfig = ®istry.AuthConfig{} - } - } - - if configFileEncoded != "" { - configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) - if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil { - // for a pull it is not an error if no auth was given - // to increase compatibility with the existing api it is defaulting to be empty - configFile = ®istry.ConfigFile{} - } - } - - if version.GreaterThanOrEqualTo("1.8") { - job.SetenvBool("json", true) - streamJSON(job, w, true) - } else { - job.Stdout.Add(utils.NewWriteFlusher(w)) - } - - if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") { - job.Setenv("rm", "1") - } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { - job.Setenv("rm", "1") - } else { - job.Setenv("rm", r.FormValue("rm")) - } - if r.FormValue("pull") == "1" && version.GreaterThanOrEqualTo("1.16") { - job.Setenv("pull", "1") - } - job.Stdin.Add(r.Body) - job.Setenv("remote", r.FormValue("remote")) - job.Setenv("dockerfile", r.FormValue("dockerfile")) - job.Setenv("t", r.FormValue("t")) - job.Setenv("q", r.FormValue("q")) - job.Setenv("nocache", r.FormValue("nocache")) - job.Setenv("forcerm", r.FormValue("forcerm")) - job.SetenvJson("authConfig", authConfig) - job.SetenvJson("configFile", configFile) - - if err := job.Run(); err != nil { - if !job.Stdout.Used() { - return err - } - sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8")) - w.Write(sf.FormatError(err)) - } - return nil -} - -func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if vars == nil { - return fmt.Errorf("Missing parameter") - } - - var copyData engine.Env - - if err := checkForJson(r); err != nil { - return err - } - - if err := copyData.Decode(r.Body); err != nil { - return err - } - - if copyData.Get("Resource") == "" { - return fmt.Errorf("Path cannot be empty") - } - - origResource := copyData.Get("Resource") - - if copyData.Get("Resource")[0] == '/' { - copyData.Set("Resource", copyData.Get("Resource")[1:]) - } - - job := eng.Job("container_copy", vars["name"], copyData.Get("Resource")) - job.Stdout.Add(w) - w.Header().Set("Content-Type", "application/x-tar") - if err := job.Run(); err != nil { - log.Errorf("%s", err.Error()) - if strings.Contains(strings.ToLower(err.Error()), "no such container") { - w.WriteHeader(http.StatusNotFound) - } else if strings.Contains(err.Error(), "no such file or directory") { - return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"]) - } - } - return nil -} - -func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return nil - } - var ( - out engine.Env - name = vars["name"] - job = eng.Job("execCreate", name) - stdoutBuffer = bytes.NewBuffer(nil) - ) - - if err := job.DecodeEnv(r.Body); err != nil { - return err - } - - job.Stdout.Add(stdoutBuffer) - // Register an instance of Exec in container. - if err := job.Run(); err != nil { - fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err) - return err - } - // Return the ID - out.Set("Id", engine.Tail(stdoutBuffer, 1)) - - return writeJSON(w, http.StatusCreated, out) -} - -// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. -func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return nil - } - var ( - name = vars["name"] - job = eng.Job("execStart", name) - errOut io.Writer = os.Stderr - ) - - if err := job.DecodeEnv(r.Body); err != nil { - return err - } - if !job.GetenvBool("Detach") { - // Setting up the streaming http interface. - inStream, outStream, err := hijackServer(w) - if err != nil { - return err - } - defer closeStreams(inStream, outStream) - - var errStream io.Writer - - if _, ok := r.Header["Upgrade"]; ok { - fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") - } else { - fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") - } - - if !job.GetenvBool("Tty") && version.GreaterThanOrEqualTo("1.6") { - errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) - outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) - } else { - errStream = outStream - } - job.Stdin.Add(inStream) - job.Stdout.Add(outStream) - job.Stderr.Set(errStream) - errOut = outStream - } - // Now run the user process in container. - job.SetCloseIO(false) - if err := job.Run(); err != nil { - fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err) - return err - } - w.WriteHeader(http.StatusNoContent) - - return nil -} - -func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := parseForm(r); err != nil { - return err - } - if vars == nil { - return fmt.Errorf("Missing parameter") - } - if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil { - return err - } - return nil -} - -func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - w.WriteHeader(http.StatusOK) - return nil -} -func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Access-Control-Allow-Origin", "*") - w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth") - w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") -} - -func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - _, err := w.Write([]byte{'O', 'K'}) - return err -} - -func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - // log the request - log.Debugf("Calling %s %s", localMethod, localRoute) - - if logging { - log.Infof("%s %s", r.Method, r.RequestURI) - } - - if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { - userAgent := strings.Split(r.Header.Get("User-Agent"), "/") - if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) { - log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion) - } - } - version := version.Version(mux.Vars(r)["version"]) - if version == "" { - version = api.APIVERSION - } - if enableCors { - writeCorsHeaders(w, r) - } - - if version.GreaterThan(api.APIVERSION) { - http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound) - return - } - - if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil { - log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) - httpError(w, err) - } - } -} - -// Replicated from expvar.go as not public. -func expvarHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprintf(w, "{\n") - first := true - expvar.Do(func(kv expvar.KeyValue) { - if !first { - fmt.Fprintf(w, ",\n") - } - first = false - fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) - }) - fmt.Fprintf(w, "\n}\n") -} - -func AttachProfiler(router *mux.Router) { - router.HandleFunc("/debug/vars", expvarHandler) - router.HandleFunc("/debug/pprof/", pprof.Index) - router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - router.HandleFunc("/debug/pprof/profile", pprof.Profile) - router.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - router.HandleFunc("/debug/pprof/block", pprof.Handler("block").ServeHTTP) - router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP) - router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP) - router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP) -} - -func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion string) *mux.Router { - r := mux.NewRouter() - if os.Getenv("DEBUG") != "" { - AttachProfiler(r) - } - m := map[string]map[string]HttpApiFunc{ - "GET": { - "/_ping": ping, - "/events": getEvents, - "/info": getInfo, - "/version": getVersion, - "/images/json": getImagesJSON, - "/images/viz": getImagesViz, - "/images/search": getImagesSearch, - "/images/get": getImagesGet, - "/images/{name:.*}/get": getImagesGet, - "/images/{name:.*}/history": getImagesHistory, - "/images/{name:.*}/json": getImagesByName, - "/containers/ps": getContainersJSON, - "/containers/json": getContainersJSON, - "/containers/{name:.*}/export": getContainersExport, - "/containers/{name:.*}/changes": getContainersChanges, - "/containers/{name:.*}/json": getContainersByName, - "/containers/{name:.*}/top": getContainersTop, - "/containers/{name:.*}/logs": getContainersLogs, - "/containers/{name:.*}/stats": getContainersStats, - "/containers/{name:.*}/attach/ws": wsContainersAttach, - "/exec/{id:.*}/json": getExecByID, - }, - "POST": { - "/auth": postAuth, - "/commit": postCommit, - "/build": postBuild, - "/images/create": postImagesCreate, - "/images/load": postImagesLoad, - "/images/{name:.*}/push": postImagesPush, - "/images/{name:.*}/tag": postImagesTag, - "/containers/create": postContainersCreate, - "/containers/{name:.*}/kill": postContainersKill, - "/containers/{name:.*}/pause": postContainersPause, - "/containers/{name:.*}/unpause": postContainersUnpause, - "/containers/{name:.*}/restart": postContainersRestart, - "/containers/{name:.*}/start": postContainersStart, - "/containers/{name:.*}/stop": postContainersStop, - "/containers/{name:.*}/wait": postContainersWait, - "/containers/{name:.*}/resize": postContainersResize, - "/containers/{name:.*}/attach": postContainersAttach, - "/containers/{name:.*}/copy": postContainersCopy, - "/containers/{name:.*}/exec": postContainerExecCreate, - "/exec/{name:.*}/start": postContainerExecStart, - "/exec/{name:.*}/resize": postContainerExecResize, - "/containers/{name:.*}/rename": postContainerRename, - }, - "DELETE": { - "/containers/{name:.*}": deleteContainers, - "/images/{name:.*}": deleteImages, - }, - "OPTIONS": { - "": optionsHandler, - }, - } - - for method, routes := range m { - for route, fct := range routes { - log.Debugf("Registering %s, %s", method, route) - // NOTE: scope issue, make sure the variables are local and won't be changed - localRoute := route - localFct := fct - localMethod := method - - // build the handler function - f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion)) - - // add the new route - if localRoute == "" { - r.Methods(localMethod).HandlerFunc(f) - } else { - r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) - r.Path(localRoute).Methods(localMethod).HandlerFunc(f) - } - } - } - - return r -} - -// ServeRequest processes a single http request to the docker remote api. -// FIXME: refactor this to be part of Server and not require re-creating a new -// router each time. This requires first moving ListenAndServe into Server. -func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) { - router := createRouter(eng, false, true, "") - // Insert APIVERSION into the request as a convenience - req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path) - router.ServeHTTP(w, req) -} - -// serveFd creates an http.Server and sets it up to serve given a socket activated -// argument. -func serveFd(addr string, job *engine.Job) error { - r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version")) - - ls, e := systemd.ListenFD(addr) - if e != nil { - return e - } - - chErrors := make(chan error, len(ls)) - - // We don't want to start serving on these sockets until the - // daemon is initialized and installed. Otherwise required handlers - // won't be ready. - <-activationLock - - // Since ListenFD will return one or more sockets we have - // to create a go func to spawn off multiple serves - for i := range ls { - listener := ls[i] - go func() { - httpSrv := http.Server{Handler: r} - chErrors <- httpSrv.Serve(listener) - }() - } - - for i := 0; i < len(ls); i++ { - err := <-chErrors - if err != nil { - return err - } - } - - return nil -} - -func lookupGidByName(nameOrGid string) (int, error) { - groupFile, err := user.GetGroupPath() - if err != nil { - return -1, err - } - groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool { - return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid - }) - if err != nil { - return -1, err - } - if groups != nil && len(groups) > 0 { - return groups[0].Gid, nil - } - return -1, fmt.Errorf("Group %s not found", nameOrGid) -} - -func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) { - tlsCert, err := tls.LoadX509KeyPair(cert, key) - if err != nil { - return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?", - cert, key, err) - } - tlsConfig := &tls.Config{ - NextProtos: []string{"http/1.1"}, - Certificates: []tls.Certificate{tlsCert}, - // Avoid fallback on insecure SSL protocols - MinVersion: tls.VersionTLS10, - } - - if ca != "" { - certPool := x509.NewCertPool() - file, err := ioutil.ReadFile(ca) - if err != nil { - return nil, fmt.Errorf("Couldn't read CA certificate: %s", err) - } - certPool.AppendCertsFromPEM(file) - tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert - tlsConfig.ClientCAs = certPool - } - - return tls.NewListener(l, tlsConfig), nil -} - -func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) { - if bufferRequests { - return listenbuffer.NewListenBuffer(proto, addr, activationLock) - } - - return net.Listen(proto, addr) -} - -func changeGroup(addr string, nameOrGid string) error { - gid, err := lookupGidByName(nameOrGid) - if err != nil { - return err - } - - log.Debugf("%s group found. gid: %d", nameOrGid, gid) - return os.Chown(addr, 0, gid) -} - -func setSocketGroup(addr, group string) error { - if group == "" { - return nil - } - - if err := changeGroup(addr, group); err != nil { - if group != "docker" { - return err - } - log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err) - } - - return nil -} - -func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) { - r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version")) - - if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) { - return nil, err - } - mask := syscall.Umask(0777) - defer syscall.Umask(mask) - - l, err := newListener("unix", addr, job.GetenvBool("BufferRequests")) - if err != nil { - return nil, err - } - - if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil { - return nil, err - } - - if err := os.Chmod(addr, 0660); err != nil { - return nil, err - } - - return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil -} - -func allocateDaemonPort(addr string) error { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return err - } - - intPort, err := strconv.Atoi(port) - if err != nil { - return err - } - - var hostIPs []net.IP - if parsedIP := net.ParseIP(host); parsedIP != nil { - hostIPs = append(hostIPs, parsedIP) - } else if hostIPs, err = net.LookupIP(host); err != nil { - return fmt.Errorf("failed to lookup %s address in host specification", host) - } - - for _, hostIP := range hostIPs { - if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil { - return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err) - } - } - return nil -} - -func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) { - if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") { - log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") - } - - r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version")) - - l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests")) - if err != nil { - return nil, err - } - - if err := allocateDaemonPort(addr); err != nil { - return nil, err - } - - if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") { - var tlsCa string - if job.GetenvBool("TlsVerify") { - tlsCa = job.Getenv("TlsCa") - } - l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l) - if err != nil { - return nil, err - } - } - return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil -} - -// NewServer sets up the required Server and does protocol specific checking. -func NewServer(proto, addr string, job *engine.Job) (Server, error) { - // Basic error and sanity checking - switch proto { - case "fd": - return nil, serveFd(addr, job) - case "tcp": - return setupTcpHttp(addr, job) - case "unix": - return setupUnixHttp(addr, job) - default: - return nil, fmt.Errorf("Invalid protocol format.") - } -} - -type Server interface { - Serve() error - Close() error -} - -// ServeApi loops through all of the protocols sent in to docker and spawns -// off a go routine to setup a serving http.Server for each. -func ServeApi(job *engine.Job) engine.Status { - if len(job.Args) == 0 { - return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name) - } - var ( - protoAddrs = job.Args - chErrors = make(chan error, len(protoAddrs)) - ) - activationLock = make(chan struct{}) - - for _, protoAddr := range protoAddrs { - protoAddrParts := strings.SplitN(protoAddr, "://", 2) - if len(protoAddrParts) != 2 { - return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name) - } - go func() { - log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) - srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job) - if err != nil { - chErrors <- err - return - } - chErrors <- srv.Serve() - }() - } - - for i := 0; i < len(protoAddrs); i++ { - err := <-chErrors - if err != nil { - return job.Error(err) - } - } - - return engine.StatusOK -} - -func AcceptConnections(job *engine.Job) engine.Status { - // Tell the init daemon we are accepting requests - go systemd.SdNotify("READY=1") - - // close the lock so the listeners start accepting connections - if activationLock != nil { - close(activationLock) - } - - return engine.StatusOK -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/server/server_unit_test.go b/Godeps/_workspace/src/github.com/docker/docker/api/server/server_unit_test.go deleted file mode 100644 index b5ec7c8964..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/server/server_unit_test.go +++ /dev/null @@ -1,553 +0,0 @@ -package server - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "testing" - - "github.com/docker/docker/api" - "github.com/docker/docker/engine" - "github.com/docker/docker/pkg/version" -) - -func TestGetBoolParam(t *testing.T) { - if ret, err := getBoolParam("true"); err != nil || !ret { - t.Fatalf("true -> true, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("True"); err != nil || !ret { - t.Fatalf("True -> true, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("1"); err != nil || !ret { - t.Fatalf("1 -> true, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam(""); err != nil || ret { - t.Fatalf("\"\" -> false, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("false"); err != nil || ret { - t.Fatalf("false -> false, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("0"); err != nil || ret { - t.Fatalf("0 -> false, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("faux"); err == nil || ret { - t.Fatalf("faux -> false, err | got %t %s", ret, err) - - } -} - -func TesthttpError(t *testing.T) { - r := httptest.NewRecorder() - - httpError(r, fmt.Errorf("No such method")) - if r.Code != http.StatusNotFound { - t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code) - } - - httpError(r, fmt.Errorf("This accound hasn't been activated")) - if r.Code != http.StatusForbidden { - t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code) - } - - httpError(r, fmt.Errorf("Some error")) - if r.Code != http.StatusInternalServerError { - t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code) - } -} - -func TestGetVersion(t *testing.T) { - eng := engine.New() - var called bool - eng.Register("version", func(job *engine.Job) engine.Status { - called = true - v := &engine.Env{} - v.SetJson("Version", "42.1") - v.Set("ApiVersion", "1.1.1.1.1") - v.Set("GoVersion", "2.42") - v.Set("Os", "Linux") - v.Set("Arch", "x86_64") - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/version", nil, eng, t) - if !called { - t.Fatalf("handler was not called") - } - v := readEnv(r.Body, t) - if v.Get("Version") != "42.1" { - t.Fatalf("%#v\n", v) - } - if r.HeaderMap.Get("Content-Type") != "application/json" { - t.Fatalf("%#v\n", r) - } -} - -func TestGetInfo(t *testing.T) { - eng := engine.New() - var called bool - eng.Register("info", func(job *engine.Job) engine.Status { - called = true - v := &engine.Env{} - v.SetInt("Containers", 1) - v.SetInt("Images", 42000) - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/info", nil, eng, t) - if !called { - t.Fatalf("handler was not called") - } - v := readEnv(r.Body, t) - if v.GetInt("Images") != 42000 { - t.Fatalf("%#v\n", v) - } - if v.GetInt("Containers") != 1 { - t.Fatalf("%#v\n", v) - } - assertContentType(r, "application/json", t) -} - -func TestGetImagesJSON(t *testing.T) { - eng := engine.New() - var called bool - eng.Register("images", func(job *engine.Job) engine.Status { - called = true - v := createEnvFromGetImagesJSONStruct(sampleImage) - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/images/json", nil, eng, t) - if !called { - t.Fatal("handler was not called") - } - assertHttpNotError(r, t) - assertContentType(r, "application/json", t) - var observed getImagesJSONStruct - if err := json.Unmarshal(r.Body.Bytes(), &observed); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(observed, sampleImage) { - t.Errorf("Expected %#v but got %#v", sampleImage, observed) - } -} - -func TestGetImagesJSONFilter(t *testing.T) { - eng := engine.New() - filter := "nothing" - eng.Register("images", func(job *engine.Job) engine.Status { - filter = job.Getenv("filter") - return engine.StatusOK - }) - serveRequest("GET", "/images/json?filter=aaaa", nil, eng, t) - if filter != "aaaa" { - t.Errorf("%#v", filter) - } -} - -func TestGetImagesJSONFilters(t *testing.T) { - eng := engine.New() - filter := "nothing" - eng.Register("images", func(job *engine.Job) engine.Status { - filter = job.Getenv("filters") - return engine.StatusOK - }) - serveRequest("GET", "/images/json?filters=nnnn", nil, eng, t) - if filter != "nnnn" { - t.Errorf("%#v", filter) - } -} - -func TestGetImagesJSONAll(t *testing.T) { - eng := engine.New() - allFilter := "-1" - eng.Register("images", func(job *engine.Job) engine.Status { - allFilter = job.Getenv("all") - return engine.StatusOK - }) - serveRequest("GET", "/images/json?all=1", nil, eng, t) - if allFilter != "1" { - t.Errorf("%#v", allFilter) - } -} - -func TestGetImagesJSONLegacyFormat(t *testing.T) { - eng := engine.New() - var called bool - eng.Register("images", func(job *engine.Job) engine.Status { - called = true - outsLegacy := engine.NewTable("Created", 0) - outsLegacy.Add(createEnvFromGetImagesJSONStruct(sampleImage)) - if _, err := outsLegacy.WriteListTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequestUsingVersion("GET", "/images/json", "1.6", nil, eng, t) - if !called { - t.Fatal("handler was not called") - } - assertHttpNotError(r, t) - assertContentType(r, "application/json", t) - images := engine.NewTable("Created", 0) - if _, err := images.ReadListFrom(r.Body.Bytes()); err != nil { - t.Fatal(err) - } - if images.Len() != 1 { - t.Fatalf("Expected 1 image, %d found", images.Len()) - } - image := images.Data[0] - if image.Get("Tag") != "test-tag" { - t.Errorf("Expected tag 'test-tag', found '%s'", image.Get("Tag")) - } - if image.Get("Repository") != "test-name" { - t.Errorf("Expected repository 'test-name', found '%s'", image.Get("Repository")) - } -} - -func TestGetContainersByName(t *testing.T) { - eng := engine.New() - name := "container_name" - var called bool - eng.Register("container_inspect", func(job *engine.Job) engine.Status { - called = true - if job.Args[0] != name { - t.Errorf("name != '%s': %#v", name, job.Args[0]) - } - if api.APIVERSION.LessThan("1.12") && !job.GetenvBool("dirty") { - t.Errorf("dirty env variable not set") - } else if api.APIVERSION.GreaterThanOrEqualTo("1.12") && job.GetenvBool("dirty") { - t.Errorf("dirty env variable set when it shouldn't") - } - v := &engine.Env{} - v.SetBool("dirty", true) - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/containers/"+name+"/json", nil, eng, t) - if !called { - t.Fatal("handler was not called") - } - assertContentType(r, "application/json", t) - var stdoutJson interface{} - if err := json.Unmarshal(r.Body.Bytes(), &stdoutJson); err != nil { - t.Fatalf("%#v", err) - } - if stdoutJson.(map[string]interface{})["dirty"].(float64) != 1 { - t.Fatalf("%#v", stdoutJson) - } -} - -func TestGetEvents(t *testing.T) { - eng := engine.New() - var called bool - eng.Register("events", func(job *engine.Job) engine.Status { - called = true - since := job.Getenv("since") - if since != "1" { - t.Fatalf("'since' should be 1, found %#v instead", since) - } - until := job.Getenv("until") - if until != "0" { - t.Fatalf("'until' should be 0, found %#v instead", until) - } - v := &engine.Env{} - v.Set("since", since) - v.Set("until", until) - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/events?since=1&until=0", nil, eng, t) - if !called { - t.Fatal("handler was not called") - } - assertContentType(r, "application/json", t) - var stdout_json struct { - Since int - Until int - } - if err := json.Unmarshal(r.Body.Bytes(), &stdout_json); err != nil { - t.Fatal(err) - } - if stdout_json.Since != 1 { - t.Errorf("since != 1: %#v", stdout_json.Since) - } - if stdout_json.Until != 0 { - t.Errorf("until != 0: %#v", stdout_json.Until) - } -} - -func TestLogs(t *testing.T) { - eng := engine.New() - var inspect bool - var logs bool - eng.Register("container_inspect", func(job *engine.Job) engine.Status { - inspect = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != "test" { - t.Fatalf("Container name %s, must be test", job.Args[0]) - } - return engine.StatusOK - }) - expected := "logs" - eng.Register("logs", func(job *engine.Job) engine.Status { - logs = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != "test" { - t.Fatalf("Container name %s, must be test", job.Args[0]) - } - follow := job.Getenv("follow") - if follow != "1" { - t.Fatalf("follow: %s, must be 1", follow) - } - stdout := job.Getenv("stdout") - if stdout != "1" { - t.Fatalf("stdout %s, must be 1", stdout) - } - stderr := job.Getenv("stderr") - if stderr != "" { - t.Fatalf("stderr %s, must be empty", stderr) - } - timestamps := job.Getenv("timestamps") - if timestamps != "1" { - t.Fatalf("timestamps %s, must be 1", timestamps) - } - job.Stdout.Write([]byte(expected)) - return engine.StatusOK - }) - r := serveRequest("GET", "/containers/test/logs?follow=1&stdout=1×tamps=1", nil, eng, t) - if r.Code != http.StatusOK { - t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK) - } - if !inspect { - t.Fatal("container_inspect job was not called") - } - if !logs { - t.Fatal("logs job was not called") - } - res := r.Body.String() - if res != expected { - t.Fatalf("Output %s, expected %s", res, expected) - } -} - -func TestLogsNoStreams(t *testing.T) { - eng := engine.New() - var inspect bool - var logs bool - eng.Register("container_inspect", func(job *engine.Job) engine.Status { - inspect = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != "test" { - t.Fatalf("Container name %s, must be test", job.Args[0]) - } - return engine.StatusOK - }) - eng.Register("logs", func(job *engine.Job) engine.Status { - logs = true - return engine.StatusOK - }) - r := serveRequest("GET", "/containers/test/logs", nil, eng, t) - if r.Code != http.StatusBadRequest { - t.Fatalf("Got status %d, expected %d", r.Code, http.StatusBadRequest) - } - if inspect { - t.Fatal("container_inspect job was called, but it shouldn't") - } - if logs { - t.Fatal("logs job was called, but it shouldn't") - } - res := strings.TrimSpace(r.Body.String()) - expected := "Bad parameters: you must choose at least one stream" - if !strings.Contains(res, expected) { - t.Fatalf("Output %s, expected %s in it", res, expected) - } -} - -func TestGetImagesHistory(t *testing.T) { - eng := engine.New() - imageName := "docker-test-image" - var called bool - eng.Register("history", func(job *engine.Job) engine.Status { - called = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != imageName { - t.Fatalf("name != '%s': %#v", imageName, job.Args[0]) - } - v := &engine.Env{} - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/images/"+imageName+"/history", nil, eng, t) - if !called { - t.Fatalf("handler was not called") - } - if r.Code != http.StatusOK { - t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK) - } - if r.HeaderMap.Get("Content-Type") != "application/json" { - t.Fatalf("%#v\n", r) - } -} - -func TestGetImagesByName(t *testing.T) { - eng := engine.New() - name := "image_name" - var called bool - eng.Register("image_inspect", func(job *engine.Job) engine.Status { - called = true - if job.Args[0] != name { - t.Fatalf("name != '%s': %#v", name, job.Args[0]) - } - if api.APIVERSION.LessThan("1.12") && !job.GetenvBool("dirty") { - t.Fatal("dirty env variable not set") - } else if api.APIVERSION.GreaterThanOrEqualTo("1.12") && job.GetenvBool("dirty") { - t.Fatal("dirty env variable set when it shouldn't") - } - v := &engine.Env{} - v.SetBool("dirty", true) - if _, err := v.WriteTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK - }) - r := serveRequest("GET", "/images/"+name+"/json", nil, eng, t) - if !called { - t.Fatal("handler was not called") - } - if r.HeaderMap.Get("Content-Type") != "application/json" { - t.Fatalf("%#v\n", r) - } - var stdoutJson interface{} - if err := json.Unmarshal(r.Body.Bytes(), &stdoutJson); err != nil { - t.Fatalf("%#v", err) - } - if stdoutJson.(map[string]interface{})["dirty"].(float64) != 1 { - t.Fatalf("%#v", stdoutJson) - } -} - -func TestDeleteContainers(t *testing.T) { - eng := engine.New() - name := "foo" - var called bool - eng.Register("rm", func(job *engine.Job) engine.Status { - called = true - if len(job.Args) == 0 { - t.Fatalf("Job arguments is empty") - } - if job.Args[0] != name { - t.Fatalf("name != '%s': %#v", name, job.Args[0]) - } - return engine.StatusOK - }) - r := serveRequest("DELETE", "/containers/"+name, nil, eng, t) - if !called { - t.Fatalf("handler was not called") - } - if r.Code != http.StatusNoContent { - t.Fatalf("Got status %d, expected %d", r.Code, http.StatusNoContent) - } -} - -func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder { - return serveRequestUsingVersion(method, target, api.APIVERSION, body, eng, t) -} - -func serveRequestUsingVersion(method, target string, version version.Version, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder { - r := httptest.NewRecorder() - req, err := http.NewRequest(method, target, body) - if err != nil { - t.Fatal(err) - } - ServeRequest(eng, version, r, req) - return r -} - -func readEnv(src io.Reader, t *testing.T) *engine.Env { - out := engine.NewOutput() - v, err := out.AddEnv() - if err != nil { - t.Fatal(err) - } - if _, err := io.Copy(out, src); err != nil { - t.Fatal(err) - } - out.Close() - return v -} - -func toJson(data interface{}, t *testing.T) io.Reader { - var buf bytes.Buffer - if err := json.NewEncoder(&buf).Encode(data); err != nil { - t.Fatal(err) - } - return &buf -} - -func assertContentType(recorder *httptest.ResponseRecorder, content_type string, t *testing.T) { - if recorder.HeaderMap.Get("Content-Type") != content_type { - t.Fatalf("%#v\n", recorder) - } -} - -// XXX: Duplicated from integration/utils_test.go, but maybe that's OK as that -// should die as soon as we converted all integration tests? -// assertHttpNotError expect the given response to not have an error. -// Otherwise the it causes the test to fail. -func assertHttpNotError(r *httptest.ResponseRecorder, t *testing.T) { - // Non-error http status are [200, 400) - if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest { - t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code)) - } -} - -func createEnvFromGetImagesJSONStruct(data getImagesJSONStruct) *engine.Env { - v := &engine.Env{} - v.SetList("RepoTags", data.RepoTags) - v.Set("Id", data.Id) - v.SetInt64("Created", data.Created) - v.SetInt64("Size", data.Size) - v.SetInt64("VirtualSize", data.VirtualSize) - return v -} - -type getImagesJSONStruct struct { - RepoTags []string - Id string - Created int64 - Size int64 - VirtualSize int64 -} - -var sampleImage getImagesJSONStruct = getImagesJSONStruct{ - RepoTags: []string{"test-name:test-tag"}, - Id: "ID", - Created: 999, - Size: 777, - VirtualSize: 666, -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/api/stats/stats.go b/Godeps/_workspace/src/github.com/docker/docker/api/stats/stats.go deleted file mode 100644 index 8edf18fe0e..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/api/stats/stats.go +++ /dev/null @@ -1,87 +0,0 @@ -// This package is used for API stability in the types and response to the -// consumers of the API stats endpoint. -package stats - -import "time" - -type ThrottlingData struct { - // Number of periods with throttling active - Periods uint64 `json:"periods"` - // Number of periods when the container hit its throttling limit. - ThrottledPeriods uint64 `json:"throttled_periods"` - // Aggregate time the container was throttled for in nanoseconds. - ThrottledTime uint64 `json:"throttled_time"` -} - -// All CPU stats are aggregated since container inception. -type CpuUsage struct { - // Total CPU time consumed. - // Units: nanoseconds. - TotalUsage uint64 `json:"total_usage"` - // Total CPU time consumed per core. - // Units: nanoseconds. - PercpuUsage []uint64 `json:"percpu_usage"` - // Time spent by tasks of the cgroup in kernel mode. - // Units: nanoseconds. - UsageInKernelmode uint64 `json:"usage_in_kernelmode"` - // Time spent by tasks of the cgroup in user mode. - // Units: nanoseconds. - UsageInUsermode uint64 `json:"usage_in_usermode"` -} - -type CpuStats struct { - CpuUsage CpuUsage `json:"cpu_usage"` - SystemUsage uint64 `json:"system_cpu_usage"` - ThrottlingData ThrottlingData `json:"throttling_data,omitempty"` -} - -type MemoryStats struct { - // current res_counter usage for memory - Usage uint64 `json:"usage"` - // maximum usage ever recorded. - MaxUsage uint64 `json:"max_usage"` - // TODO(vishh): Export these as stronger types. - // all the stats exported via memory.stat. - Stats map[string]uint64 `json:"stats"` - // number of times memory usage hits limits. - Failcnt uint64 `json:"failcnt"` - Limit uint64 `json:"limit"` -} - -type BlkioStatEntry struct { - Major uint64 `json:"major"` - Minor uint64 `json:"minor"` - Op string `json:"op"` - Value uint64 `json:"value"` -} - -type BlkioStats struct { - // number of bytes tranferred to and from the block device - IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"` - IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"` - IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"` - IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"` - IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"` - IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"` - IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"` - SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"` -} - -type Network struct { - RxBytes uint64 `json:"rx_bytes"` - RxPackets uint64 `json:"rx_packets"` - RxErrors uint64 `json:"rx_errors"` - RxDropped uint64 `json:"rx_dropped"` - TxBytes uint64 `json:"tx_bytes"` - TxPackets uint64 `json:"tx_packets"` - TxErrors uint64 `json:"tx_errors"` - TxDropped uint64 `json:"tx_dropped"` -} - -type Stats struct { - Read time.Time `json:"read"` - Network Network `json:"network,omitempty"` - CpuStats CpuStats `json:"cpu_stats,omitempty"` - MemoryStats MemoryStats `json:"memory_stats,omitempty"` - BlkioStats BlkioStats `json:"blkio_stats,omitempty"` -} diff --git a/commands/active.go b/commands/active.go index 6840806a67..7bebc37ac4 100644 --- a/commands/active.go +++ b/commands/active.go @@ -3,9 +3,8 @@ package commands import ( "fmt" - log "github.com/Sirupsen/logrus" - "github.com/codegangsta/cli" + "github.com/docker/machine/log" ) func cmdActive(c *cli.Context) { diff --git a/commands/commands.go b/commands/commands.go index 03740c7afb..a4dadb3075 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -9,7 +9,6 @@ import ( "sort" "strings" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/skarademir/naturalsort" @@ -31,6 +30,7 @@ import ( "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/log" "github.com/docker/machine/state" "github.com/docker/machine/utils" ) diff --git a/commands/config.go b/commands/config.go index fdfa9fe0e6..e77cfc4043 100644 --- a/commands/config.go +++ b/commands/config.go @@ -5,9 +5,8 @@ import ( "net/url" "strings" - log "github.com/Sirupsen/logrus" - "github.com/codegangsta/cli" + "github.com/docker/machine/log" "github.com/docker/machine/utils" ) diff --git a/commands/create.go b/commands/create.go index 23d2d07e4e..9b27cd0dbe 100644 --- a/commands/create.go +++ b/commands/create.go @@ -4,7 +4,7 @@ import ( "fmt" "path/filepath" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" diff --git a/commands/env.go b/commands/env.go index 79059c7c12..81a52eec72 100644 --- a/commands/env.go +++ b/commands/env.go @@ -7,7 +7,7 @@ import ( "strings" "text/template" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" "github.com/docker/machine/utils" diff --git a/commands/inspect.go b/commands/inspect.go index 6d5787c0a9..760773e8f7 100644 --- a/commands/inspect.go +++ b/commands/inspect.go @@ -6,7 +6,7 @@ import ( "os" "text/template" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/ip.go b/commands/ip.go index b6b9fb5c71..5a8f68aece 100644 --- a/commands/ip.go +++ b/commands/ip.go @@ -1,9 +1,8 @@ package commands import ( - log "github.com/Sirupsen/logrus" - "github.com/codegangsta/cli" + "github.com/docker/machine/log" ) func cmdIp(c *cli.Context) { diff --git a/commands/kill.go b/commands/kill.go index 8bed899f10..607b3475b8 100644 --- a/commands/kill.go +++ b/commands/kill.go @@ -1,7 +1,7 @@ package commands import ( - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/ls.go b/commands/ls.go index 6bf5a1927f..aafebdcc8a 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -5,7 +5,7 @@ import ( "os" "text/tabwriter" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/regeneratecerts.go b/commands/regeneratecerts.go index 0a4b35f2d2..930178fa04 100644 --- a/commands/regeneratecerts.go +++ b/commands/regeneratecerts.go @@ -1,8 +1,8 @@ package commands import ( - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/docker/machine/log" ) func cmdRegenerateCerts(c *cli.Context) { diff --git a/commands/restart.go b/commands/restart.go index 99b8365315..9b1cb4cd38 100644 --- a/commands/restart.go +++ b/commands/restart.go @@ -1,7 +1,7 @@ package commands import ( - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/rm.go b/commands/rm.go index 0d181fb8ba..b8ce7ea9ee 100644 --- a/commands/rm.go +++ b/commands/rm.go @@ -1,8 +1,8 @@ package commands import ( - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/docker/machine/log" ) func cmdRm(c *cli.Context) { diff --git a/commands/ssh.go b/commands/ssh.go index 35a7206487..392d3e3f40 100644 --- a/commands/ssh.go +++ b/commands/ssh.go @@ -5,7 +5,7 @@ import ( "os" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" diff --git a/commands/start.go b/commands/start.go index d82ed69c00..161e67ae7a 100644 --- a/commands/start.go +++ b/commands/start.go @@ -1,7 +1,7 @@ package commands import ( - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/stop.go b/commands/stop.go index f5d51751f4..7a47818a85 100644 --- a/commands/stop.go +++ b/commands/stop.go @@ -1,7 +1,7 @@ package commands import ( - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/upgrade.go b/commands/upgrade.go index 5326aefee9..6e22c00cea 100644 --- a/commands/upgrade.go +++ b/commands/upgrade.go @@ -1,7 +1,7 @@ package commands import ( - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/commands/url.go b/commands/url.go index 60f6377af5..a714c1c486 100644 --- a/commands/url.go +++ b/commands/url.go @@ -3,7 +3,7 @@ package commands import ( "fmt" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" ) diff --git a/drivers/amazonec2/amazonec2.go b/drivers/amazonec2/amazonec2.go index e9781acc61..4b379c554b 100644 --- a/drivers/amazonec2/amazonec2.go +++ b/drivers/amazonec2/amazonec2.go @@ -12,10 +12,10 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" "github.com/docker/machine/drivers/amazonec2/amz" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/azure/azure.go b/drivers/azure/azure.go index 3726c008b1..0dee97b62e 100644 --- a/drivers/azure/azure.go +++ b/drivers/azure/azure.go @@ -11,13 +11,13 @@ import ( azure "github.com/MSOpenTech/azure-sdk-for-go" "github.com/MSOpenTech/azure-sdk-for-go/clients/vmClient" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/docker/docker/utils" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" + "github.com/docker/machine/utils" ) type Driver struct { diff --git a/drivers/digitalocean/digitalocean.go b/drivers/digitalocean/digitalocean.go index 7348af2b76..7aa0baf3b3 100644 --- a/drivers/digitalocean/digitalocean.go +++ b/drivers/digitalocean/digitalocean.go @@ -7,10 +7,10 @@ import ( "time" "code.google.com/p/goauth2/oauth" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/digitalocean/godo" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/drivers.go b/drivers/drivers.go index 2b432624be..6b43f501cd 100644 --- a/drivers/drivers.go +++ b/drivers/drivers.go @@ -5,8 +5,8 @@ import ( "fmt" "sort" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/google/auth_util.go b/drivers/google/auth_util.go index 14122b0a52..b8d5bd0869 100644 --- a/drivers/google/auth_util.go +++ b/drivers/google/auth_util.go @@ -12,7 +12,7 @@ import ( "time" "code.google.com/p/goauth2/oauth" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" raw "google.golang.org/api/compute/v1" ) diff --git a/drivers/google/compute_util.go b/drivers/google/compute_util.go index ddf309259c..015dc8ed65 100644 --- a/drivers/google/compute_util.go +++ b/drivers/google/compute_util.go @@ -7,7 +7,7 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/docker/machine/ssh" raw "google.golang.org/api/compute/v1" ) diff --git a/drivers/google/google.go b/drivers/google/google.go index ace15d33dc..b5863ca9ad 100644 --- a/drivers/google/google.go +++ b/drivers/google/google.go @@ -4,9 +4,9 @@ import ( "fmt" "path/filepath" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/hyperv/hyperv_windows.go b/drivers/hyperv/hyperv_windows.go index a9ca9b5628..1d719392c0 100644 --- a/drivers/hyperv/hyperv_windows.go +++ b/drivers/hyperv/hyperv_windows.go @@ -9,9 +9,9 @@ import ( "path/filepath" "time" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/hyperv/powershell_windows.go b/drivers/hyperv/powershell_windows.go index a0bd4a2d3b..781175586e 100644 --- a/drivers/hyperv/powershell_windows.go +++ b/drivers/hyperv/powershell_windows.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) var powershell string diff --git a/drivers/none/none.go b/drivers/none/none.go index 1b8a275fb3..e923aa7d2d 100644 --- a/drivers/none/none.go +++ b/drivers/none/none.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/codegangsta/cli" - "github.com/docker/docker/api" "github.com/docker/machine/drivers" "github.com/docker/machine/provider" "github.com/docker/machine/state" @@ -112,12 +111,8 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { if url == "" { return fmt.Errorf("--url option is required when no driver is selected") } - validatedUrl, err := api.ValidateHost(url) - if err != nil { - return err - } - d.URL = validatedUrl + d.URL = url return nil } diff --git a/drivers/openstack/client.go b/drivers/openstack/client.go index 711329652f..e464a5674c 100644 --- a/drivers/openstack/client.go +++ b/drivers/openstack/client.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/docker/machine/version" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack" diff --git a/drivers/openstack/openstack.go b/drivers/openstack/openstack.go index 3714fb1a93..3349b33264 100644 --- a/drivers/openstack/openstack.go +++ b/drivers/openstack/openstack.go @@ -7,13 +7,13 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/docker/docker/utils" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" + "github.com/docker/machine/utils" ) type Driver struct { diff --git a/drivers/rackspace/client.go b/drivers/rackspace/client.go index 79c3241d5d..bca5a784cf 100644 --- a/drivers/rackspace/client.go +++ b/drivers/rackspace/client.go @@ -3,8 +3,8 @@ package rackspace import ( "fmt" - log "github.com/Sirupsen/logrus" "github.com/docker/machine/drivers/openstack" + "github.com/docker/machine/log" "github.com/docker/machine/version" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/rackspace" diff --git a/drivers/rackspace/rackspace.go b/drivers/rackspace/rackspace.go index ff9d0cc32b..cbb7cedaf5 100644 --- a/drivers/rackspace/rackspace.go +++ b/drivers/rackspace/rackspace.go @@ -3,10 +3,10 @@ package rackspace import ( "fmt" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" "github.com/docker/machine/drivers/openstack" + "github.com/docker/machine/log" ) // Driver is a machine driver for Rackspace. It's a specialization of the generic OpenStack one. diff --git a/drivers/softlayer/driver.go b/drivers/softlayer/driver.go index 3d13193673..b223986c47 100644 --- a/drivers/softlayer/driver.go +++ b/drivers/softlayer/driver.go @@ -8,9 +8,9 @@ import ( "regexp" "time" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/virtualbox/vbm.go b/drivers/virtualbox/vbm.go index 21a94e3172..a6f9434adf 100644 --- a/drivers/virtualbox/vbm.go +++ b/drivers/virtualbox/vbm.go @@ -11,7 +11,7 @@ import ( "runtime" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) var ( diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index dcaa0c5171..109090fc82 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -16,9 +16,9 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/vmwarefusion/fusion_darwin.go b/drivers/vmwarefusion/fusion_darwin.go index 3cc9c92e32..99ebffc80a 100644 --- a/drivers/vmwarefusion/fusion_darwin.go +++ b/drivers/vmwarefusion/fusion_darwin.go @@ -19,9 +19,9 @@ import ( "text/template" "time" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" diff --git a/drivers/vmwarefusion/vmrun_darwin.go b/drivers/vmwarefusion/vmrun_darwin.go index 4850a81b06..917c1347fd 100644 --- a/drivers/vmwarefusion/vmrun_darwin.go +++ b/drivers/vmwarefusion/vmrun_darwin.go @@ -12,7 +12,7 @@ import ( "os/exec" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) var ( diff --git a/drivers/vmwarevcloudair/vcloudair.go b/drivers/vmwarevcloudair/vcloudair.go index 3b6df907cd..8ee2ce6b86 100644 --- a/drivers/vmwarevcloudair/vcloudair.go +++ b/drivers/vmwarevcloudair/vcloudair.go @@ -12,13 +12,13 @@ import ( "github.com/vmware/govcloudair" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/docker/docker/utils" "github.com/docker/machine/drivers" + "github.com/docker/machine/log" "github.com/docker/machine/provider" "github.com/docker/machine/ssh" "github.com/docker/machine/state" + "github.com/docker/machine/utils" ) type Driver struct { diff --git a/drivers/vmwarevsphere/govc.go b/drivers/vmwarevsphere/govc.go index 6a7165e7fa..c8612e8fec 100644 --- a/drivers/vmwarevsphere/govc.go +++ b/drivers/vmwarevsphere/govc.go @@ -9,7 +9,7 @@ import ( "os/exec" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) var ( diff --git a/drivers/vmwarevsphere/vc_conn.go b/drivers/vmwarevsphere/vc_conn.go index 02f838645f..ba62d1f2ff 100644 --- a/drivers/vmwarevsphere/vc_conn.go +++ b/drivers/vmwarevsphere/vc_conn.go @@ -9,8 +9,8 @@ import ( "strconv" "strings" - log "github.com/Sirupsen/logrus" "github.com/docker/machine/drivers/vmwarevsphere/errors" + "github.com/docker/machine/log" ) type VcConn struct { diff --git a/drivers/vmwarevsphere/vsphere.go b/drivers/vmwarevsphere/vsphere.go index 4df158ad93..c4b6ce1b0a 100644 --- a/drivers/vmwarevsphere/vsphere.go +++ b/drivers/vmwarevsphere/vsphere.go @@ -16,7 +16,7 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" diff --git a/libmachine/filestore.go b/libmachine/filestore.go index b5965f7f21..a9fde8fbaf 100644 --- a/libmachine/filestore.go +++ b/libmachine/filestore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" "github.com/docker/machine/utils" ) diff --git a/libmachine/host.go b/libmachine/host.go index 5c04e0fd64..a44c8508a8 100644 --- a/libmachine/host.go +++ b/libmachine/host.go @@ -8,13 +8,13 @@ import ( "path/filepath" "regexp" - log "github.com/Sirupsen/logrus" "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/engine" "github.com/docker/machine/libmachine/provision" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/log" "github.com/docker/machine/ssh" "github.com/docker/machine/state" "github.com/docker/machine/utils" diff --git a/libmachine/provision/boot2docker.go b/libmachine/provision/boot2docker.go index 3f2a32d30c..9677364e3b 100644 --- a/libmachine/provision/boot2docker.go +++ b/libmachine/provision/boot2docker.go @@ -7,12 +7,12 @@ import ( "path" "text/template" - log "github.com/Sirupsen/logrus" "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/engine" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/log" "github.com/docker/machine/ssh" "github.com/docker/machine/state" "github.com/docker/machine/utils" diff --git a/libmachine/provision/os_release.go b/libmachine/provision/os_release.go index 464f435317..f61083021b 100644 --- a/libmachine/provision/os_release.go +++ b/libmachine/provision/os_release.go @@ -7,7 +7,7 @@ import ( "reflect" "strings" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) // The /etc/os-release file contains operating system identification data diff --git a/libmachine/provision/ubuntu.go b/libmachine/provision/ubuntu.go index b803afbf17..82c5a1ee23 100644 --- a/libmachine/provision/ubuntu.go +++ b/libmachine/provision/ubuntu.go @@ -5,12 +5,12 @@ import ( "fmt" "text/template" - log "github.com/Sirupsen/logrus" "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/engine" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/log" "github.com/docker/machine/ssh" "github.com/docker/machine/utils" ) diff --git a/libmachine/provision/utils.go b/libmachine/provision/utils.go index 4429a97a6e..9214b8e06b 100644 --- a/libmachine/provision/utils.go +++ b/libmachine/provision/utils.go @@ -10,10 +10,10 @@ import ( "strconv" "strings" - log "github.com/Sirupsen/logrus" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/log" "github.com/docker/machine/utils" ) diff --git a/log.go b/log.go deleted file mode 100644 index cdbbd4408f..0000000000 --- a/log.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "os" - - log "github.com/Sirupsen/logrus" -) - -func initLogging(lvl log.Level) { - log.SetOutput(os.Stderr) - log.SetLevel(lvl) -} diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000000..e7e900c032 --- /dev/null +++ b/log/log.go @@ -0,0 +1,123 @@ +package log + +import ( + "fmt" + "os" + "strconv" +) + +// Why the interface? We may only want to print to STDOUT and STDERR for now, +// but it won't neccessarily be that way forever. This interface is intended +// to provide a "framework" for a variety of different logging types in the +// future (log to file, log to logstash, etc.) There could be a driver model +// similar to what is done with OS or machine providers. +type Logger interface { + Debug(...interface{}) + Debugf(string, ...interface{}) + + Error(...interface{}) + Errorf(string, ...interface{}) + Errorln(...interface{}) + + Info(...interface{}) + Infof(string, ...interface{}) + Infoln(...interface{}) + + Fatal(...interface{}) + Fatalf(string, ...interface{}) + + Print(...interface{}) + Printf(string, ...interface{}) + + Warn(...interface{}) + Warnf(string, ...interface{}) + + WithFields(Fields) Logger +} + +var ( + l = TerminalLogger{} +) + +// TODO: I think this is superflous and can be replaced by one check for if +// debug is on that sets a variable in this module. +func isDebug() bool { + debugEnv := os.Getenv("DEBUG") + if debugEnv != "" { + showDebug, err := strconv.ParseBool(debugEnv) + if err != nil { + fmt.Fprintln(os.Stderr, "Error parsing boolean value from DEBUG: %s", err) + os.Exit(1) + } + return showDebug + } + return false +} + +type Fields map[string]interface{} + +func Debug(args ...interface{}) { + l.Debug(args...) +} + +func Debugf(fmtString string, args ...interface{}) { + l.Debugf(fmtString, args...) +} + +func Error(args ...interface{}) { + l.Error(args...) +} + +func Errorf(fmtString string, args ...interface{}) { + l.Errorf(fmtString, args...) +} + +func Errorln(args ...interface{}) { + l.Errorln(args...) +} + +func Info(args ...interface{}) { + l.Info(args...) +} + +func Infof(fmtString string, args ...interface{}) { + l.Infof(fmtString, args...) +} + +func Infoln(args ...interface{}) { + l.Infoln(args...) +} + +func Fatal(args ...interface{}) { + l.Fatal(args...) +} + +func Fatalf(fmtString string, args ...interface{}) { + l.Fatalf(fmtString, args...) +} + +func Print(args ...interface{}) { + l.Print(args...) +} + +func Printf(fmtString string, args ...interface{}) { + l.Printf(fmtString, args...) +} + +func Warn(args ...interface{}) { + l.Warn(args...) +} + +func Warnf(fmtString string, args ...interface{}) { + l.Warnf(fmtString, args...) +} + +func WithField(fieldName string, field interface{}) Logger { + return l.WithFields(Fields{ + fieldName: field, + }) +} + +func WithFields(fields Fields) Logger { + return l.WithFields(fields) +} diff --git a/log/log_test.go b/log/log_test.go new file mode 100644 index 0000000000..349ffe6e0e --- /dev/null +++ b/log/log_test.go @@ -0,0 +1,19 @@ +package log + +import "testing" + +func TestTerminalLoggerWithFields(t *testing.T) { + logger := TerminalLogger{} + withFieldsLogger := logger.WithFields(Fields{ + "foo": "bar", + "spam": "eggs", + }) + withFieldsTerminalLogger, ok := withFieldsLogger.(TerminalLogger) + if !ok { + t.Fatal("Type assertion to TerminalLogger failed") + } + expectedOutFields := "\t\t foo=bar spam=eggs" + if withFieldsTerminalLogger.fieldOut != expectedOutFields { + t.Fatalf("Expected %q, got %q", expectedOutFields, withFieldsTerminalLogger.fieldOut) + } +} diff --git a/log/terminal.go b/log/terminal.go new file mode 100644 index 0000000000..57f26fdb36 --- /dev/null +++ b/log/terminal.go @@ -0,0 +1,129 @@ +package log + +import ( + "fmt" + "os" + "sort" +) + +type TerminalLogger struct { + // fieldOut is used to do log.WithFields correctly + fieldOut string +} + +func (t TerminalLogger) log(args ...interface{}) { + fmt.Print(args...) + fmt.Print(t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t TerminalLogger) logf(fmtString string, args ...interface{}) { + fmt.Printf(fmtString, args...) + fmt.Print(t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t TerminalLogger) err(args ...interface{}) { + fmt.Fprint(os.Stderr, args...) + fmt.Fprint(os.Stderr, t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t TerminalLogger) errf(fmtString string, args ...interface{}) { + fmt.Fprintf(os.Stderr, fmtString, args...) + fmt.Print(t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t TerminalLogger) Debug(args ...interface{}) { + if isDebug() { + t.log(args...) + } +} + +func (t TerminalLogger) Debugf(fmtString string, args ...interface{}) { + if isDebug() { + t.logf(fmtString, args...) + } +} + +func (t TerminalLogger) Error(args ...interface{}) { + t.err(args...) +} + +func (t TerminalLogger) Errorf(fmtString string, args ...interface{}) { + t.errf(fmtString, args...) +} + +func (t TerminalLogger) Errorln(args ...interface{}) { + t.err(args...) +} + +func (t TerminalLogger) Info(args ...interface{}) { + t.log(args...) +} + +func (t TerminalLogger) Infof(fmtString string, args ...interface{}) { + t.logf(fmtString, args...) +} + +func (t TerminalLogger) Infoln(args ...interface{}) { + t.log(args...) +} + +func (t TerminalLogger) Fatal(args ...interface{}) { + t.err(args...) + os.Exit(1) +} + +func (t TerminalLogger) Fatalf(fmtString string, args ...interface{}) { + t.errf(fmtString, args...) + os.Exit(1) +} + +func (t TerminalLogger) Print(args ...interface{}) { + t.log(args...) +} + +func (t TerminalLogger) Printf(fmtString string, args ...interface{}) { + t.logf(fmtString, args...) +} + +func (t TerminalLogger) Warn(args ...interface{}) { + t.log(args...) +} + +func (t TerminalLogger) Warnf(fmtString string, args ...interface{}) { + t.logf(fmtString, args...) +} + +func (t TerminalLogger) WithFields(fields Fields) Logger { + // When the user calls WithFields, we make a string which gets appended + // to the output of the final [Info|Warn|Error] call for the + // descriptive fields. Because WithFields returns the proper Logger + // (with the fieldOut string set correctly), the logrus syntax will + // still work. + kvpairs := []string{} + + // Why the string slice song and dance? Because Go's map iteration + // order is random, we will get inconsistent results if we don't sort + // the fields (or their resulting string K/V pairs, like we have here). + // Otherwise, we couldn't test this reliably. + for k, v := range fields { + kvpairs = append(kvpairs, fmt.Sprintf("%s=%v", k, v)) + } + + sort.Strings(kvpairs) + + // TODO: + // 1. Is this thread-safe? + // 2. Add more tabs? + t.fieldOut = "\t\t" + + for _, s := range kvpairs { + // TODO: Is %v the correct format string here? + t.fieldOut = fmt.Sprintf("%s %s", t.fieldOut, s) + } + + return t +} diff --git a/main.go b/main.go index 1b0b4473be..ba1e6b2f50 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,10 @@ import ( "os" "path" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/commands" + "github.com/docker/machine/log" "github.com/docker/machine/utils" "github.com/docker/machine/version" ) @@ -49,7 +49,6 @@ func main() { for _, f := range os.Args { if f == "-D" || f == "--debug" || f == "-debug" { os.Setenv("DEBUG", "1") - initLogging(log.DebugLevel) } } diff --git a/ssh/ssh.go b/ssh/ssh.go index cd3f6b74f9..baaf92443a 100644 --- a/ssh/ssh.go +++ b/ssh/ssh.go @@ -1,8 +1,6 @@ package ssh -import ( - "net" -) +import "net" func WaitForTCP(addr string) error { for { diff --git a/utils/b2d.go b/utils/b2d.go index 38df28870c..853880a831 100644 --- a/utils/b2d.go +++ b/utils/b2d.go @@ -12,7 +12,7 @@ import ( "path/filepath" "time" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) const ( diff --git a/utils/utils.go b/utils/utils.go index c4899ef906..1ee33828b3 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,6 +1,8 @@ package utils import ( + "crypto/rand" + "encoding/hex" "encoding/json" "fmt" "io" @@ -8,9 +10,10 @@ import ( "os" "path/filepath" "runtime" + "strconv" "time" - log "github.com/Sirupsen/logrus" + "github.com/docker/machine/log" ) func GetHomeDir() string { @@ -126,3 +129,32 @@ func DumpVal(vals ...interface{}) { log.Debug(string(prettyJSON)) } } + +// Following two functions are from github.com/docker/docker/utils module. It +// was way overkill to include the whole module, so we just have these bits +// that we're using here. +func TruncateID(id string) string { + shortLen := 12 + if len(id) < shortLen { + shortLen = len(id) + } + return id[:shortLen] +} + +// GenerateRandomID returns an unique id +func GenerateRandomID() string { + for { + id := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, id); err != nil { + panic(err) // This shouldn't happen + } + value := hex.EncodeToString(id) + // if we try to parse the truncated for as an int and we don't have + // an error then the value is all numberic and causes issues when + // used as a hostname. ref #3869 + if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil { + continue + } + return value + } +}