diff --git a/Dockerfile b/Dockerfile index 109e4268..66504959 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,8 @@ -FROM golang:1.5.1 +FROM golang:1.6 -# Copy latest docker client(s) -COPY ./docker/docker-1.9.1 /bin/docker-1.9.1 +# Copy latest docker client inside the container +COPY ./docker/docker-1.10.2 /bin/docker-1.10.2 +# and make it executable RUN chmod +x /bin/docker-* # Copy Go code and install applications diff --git a/docker/docker-1.9.1 b/docker/docker-1.10.2 similarity index 68% rename from docker/docker-1.9.1 rename to docker/docker-1.10.2 index 8090020c..1e1895f9 100644 Binary files a/docker/docker-1.9.1 and b/docker/docker-1.10.2 differ diff --git a/go/src/github.com/Sirupsen/logrus/.gitignore b/go/src/github.com/Sirupsen/logrus/.gitignore deleted file mode 100644 index 66be63a0..00000000 --- a/go/src/github.com/Sirupsen/logrus/.gitignore +++ /dev/null @@ -1 +0,0 @@ -logrus diff --git a/go/src/github.com/Sirupsen/logrus/.travis.yml b/go/src/github.com/Sirupsen/logrus/.travis.yml deleted file mode 100644 index ec641142..00000000 --- a/go/src/github.com/Sirupsen/logrus/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go -go: - - 1.3 - - 1.4 - - tip -install: - - go get -t ./... diff --git a/go/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go b/go/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go deleted file mode 100644 index d20a0f54..00000000 --- a/go/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go +++ /dev/null @@ -1,68 +0,0 @@ -package logrus_bugsnag - -import ( - "errors" - - "github.com/Sirupsen/logrus" - "github.com/bugsnag/bugsnag-go" -) - -type bugsnagHook struct{} - -// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before -// bugsnag.Configure. Bugsnag must be configured before the hook. -var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook") - -// ErrBugsnagSendFailed indicates that the hook failed to submit an error to -// bugsnag. The error was successfully generated, but `bugsnag.Notify()` -// failed. -type ErrBugsnagSendFailed struct { - err error -} - -func (e ErrBugsnagSendFailed) Error() string { - return "failed to send error to Bugsnag: " + e.err.Error() -} - -// NewBugsnagHook initializes a logrus hook which sends exceptions to an -// exception-tracking service compatible with the Bugsnag API. Before using -// this hook, you must call bugsnag.Configure(). The returned object should be -// registered with a log via `AddHook()` -// -// Entries that trigger an Error, Fatal or Panic should now include an "error" -// field to send to Bugsnag. -func NewBugsnagHook() (*bugsnagHook, error) { - if bugsnag.Config.APIKey == "" { - return nil, ErrBugsnagUnconfigured - } - return &bugsnagHook{}, nil -} - -// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the -// "error" field (or the Message if the error isn't present) and sends it off. -func (hook *bugsnagHook) Fire(entry *logrus.Entry) error { - var notifyErr error - err, ok := entry.Data["error"].(error) - if ok { - notifyErr = err - } else { - notifyErr = errors.New(entry.Message) - } - - bugsnagErr := bugsnag.Notify(notifyErr) - if bugsnagErr != nil { - return ErrBugsnagSendFailed{bugsnagErr} - } - - return nil -} - -// Levels enumerates the log levels on which the error should be forwarded to -// bugsnag: everything at or above the "Error" level. -func (hook *bugsnagHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.ErrorLevel, - logrus.FatalLevel, - logrus.PanicLevel, - } -} diff --git a/go/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go b/go/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go deleted file mode 100644 index e9ea298d..00000000 --- a/go/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package logrus_bugsnag - -import ( - "encoding/json" - "errors" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Sirupsen/logrus" - "github.com/bugsnag/bugsnag-go" -) - -type notice struct { - Events []struct { - Exceptions []struct { - Message string `json:"message"` - } `json:"exceptions"` - } `json:"events"` -} - -func TestNoticeReceived(t *testing.T) { - msg := make(chan string, 1) - expectedMsg := "foo" - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var notice notice - data, _ := ioutil.ReadAll(r.Body) - if err := json.Unmarshal(data, ¬ice); err != nil { - t.Error(err) - } - _ = r.Body.Close() - - msg <- notice.Events[0].Exceptions[0].Message - })) - defer ts.Close() - - hook := &bugsnagHook{} - - bugsnag.Configure(bugsnag.Configuration{ - Endpoint: ts.URL, - ReleaseStage: "production", - APIKey: "12345678901234567890123456789012", - Synchronous: true, - }) - - log := logrus.New() - log.Hooks.Add(hook) - - log.WithFields(logrus.Fields{ - "error": errors.New(expectedMsg), - }).Error("Bugsnag will not see this string") - - select { - case received := <-msg: - if received != expectedMsg { - t.Errorf("Unexpected message received: %s", received) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Bugsnag API") - } -} diff --git a/go/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md b/go/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md deleted file mode 100644 index ae61e922..00000000 --- a/go/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/go/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go b/go/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go deleted file mode 100644 index c0f10c1b..00000000 --- a/go/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go +++ /dev/null @@ -1,55 +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) - msg, _ := entry.String() - payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg) - - 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/go/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go b/go/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go deleted file mode 100644 index 96318d00..00000000 --- a/go/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/go/src/github.com/samalba/dockerclient/.gitignore b/go/src/github.com/samalba/dockerclient/.gitignore deleted file mode 100755 index 00268614..00000000 --- a/go/src/github.com/samalba/dockerclient/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe diff --git a/go/src/goproxy/main.go b/go/src/goproxy/main.go index 7b11e026..6b635484 100644 --- a/go/src/goproxy/main.go +++ b/go/src/goproxy/main.go @@ -27,18 +27,21 @@ import ( // instance of DockerClient allowing for making calls to the docker daemon // remote API var dockerClient *dockerclient.DockerClient -// version of the docker daemon which is exposing the remote API + +// version of the docker daemon goproxy is connected to var dockerDaemonVersion string +// type CPUStats struct { TotalUsage uint64 SystemUsage uint64 } -// previousCPUStats is a map containing the previous CPU stats we got from the +// previousCPUStats is a map containing the previous CPU stats we got from the // docker daemon through the docker remote API var previousCPUStats map[string]*CPUStats = make(map[string]*CPUStats) +// main function of goproxy func main() { // goproxy is executed as a short lived process to send a request to the @@ -59,6 +62,8 @@ func main() { return } + logrus.Println("[goproxy] starting dockercraft goproxy daemon...") + // init docker client object var err error dockerClient, err = dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil) @@ -89,98 +94,133 @@ func main() { // eventCallback receives and handles the docker events func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) { - logrus.Debugln("--\n%+v", *event) - - id := event.Id - - switch event.Status { - case "create": - logrus.Debugln("create event") - - repo, tag := splitRepoAndTag(event.From) - containerName := "" - containerInfo, err := dockerClient.InspectContainer(id) - if err != nil { - logrus.Print("InspectContainer error:", err.Error()) - } else { - containerName = containerInfo.Name - } - - data := url.Values{ - "action": {"createContainer"}, - "id": {id}, - "name": {containerName}, - "imageRepo": {repo}, - "imageTag": {tag}} - - CuberiteServerRequest(data) - - case "start": - logrus.Debugln("start event") - - repo, tag := splitRepoAndTag(event.From) - containerName := "" - containerInfo, err := dockerClient.InspectContainer(id) - if err != nil { - logrus.Print("InspectContainer error:", err.Error()) - } else { - containerName = containerInfo.Name - } - - data := url.Values{ - "action": {"startContainer"}, - "id": {id}, - "name": {containerName}, - "imageRepo": {repo}, - "imageTag": {tag}} - - // Monitor stats - dockerClient.StartMonitorStats(id, statCallback, nil) - CuberiteServerRequest(data) - - case "stop": - // die event is enough - // http://docs.docker.com/reference/api/docker_remote_api/#docker-events - - case "restart": - // start event is enough - // http://docs.docker.com/reference/api/docker_remote_api/#docker-events - - case "kill": - // die event is enough - // http://docs.docker.com/reference/api/docker_remote_api/#docker-events - - case "die": - logrus.Debugln("die event") - - // same as stop event - repo, tag := splitRepoAndTag(event.From) - containerName := "" - containerInfo, err := dockerClient.InspectContainer(id) - if err != nil { - logrus.Print("InspectContainer error:", err.Error()) - } else { - containerName = containerInfo.Name + // logrus.Println("[goproxy] [event] ----- event -----") + // logrus.Println("[goproxy] [event] | type :", event.Type) + // logrus.Println("[goproxy] [event] | action:", event.Action) + + // handle different kind of events + switch event.Type { + case "container": + // TODO: gdevillele: maybe check for "event.Action" instead of + // "event.Status" for event of type "container" + switch event.Status { + case "create": + // logrus.Println("[goproxy] [event received] create") + // get container ID + containerId := event.ID + repo, tag := splitRepoAndTag(event.From) + containerName := "" + containerInfo, err := dockerClient.InspectContainer(containerId) + if err != nil { + logrus.Print("InspectContainer error:", err.Error()) + } else { + containerName = containerInfo.Name + } + data := url.Values{ + "action": {"createContainer"}, + "id": {containerId}, + "name": {containerName}, + "imageRepo": {repo}, + "imageTag": {tag}} + CuberiteServerRequest(data) + case "start": + // logrus.Println("[goproxy] [event received] start") + // get container ID + containerId := event.ID + repo, tag := splitRepoAndTag(event.From) + containerName := "" + containerInfo, err := dockerClient.InspectContainer(containerId) + if err != nil { + logrus.Print("InspectContainer error:", err.Error()) + } else { + containerName = containerInfo.Name + } + data := url.Values{ + "action": {"startContainer"}, + "id": {containerId}, + "name": {containerName}, + "imageRepo": {repo}, + "imageTag": {tag}} + // Monitor stats + dockerClient.StartMonitorStats(containerId, statCallback, nil) + CuberiteServerRequest(data) + case "stop": + // die event is enough + // http://docs.docker.com/reference/api/docker_remote_api/#docker-events + case "restart": + // start event is enough + // http://docs.docker.com/reference/api/docker_remote_api/#docker-events + case "kill": + // die event is enough + // http://docs.docker.com/reference/api/docker_remote_api/#docker-events + case "die": + // logrus.Println("[goproxy] [event received] die") + // same as stop event + // get container ID + containerId := event.ID + repo, tag := splitRepoAndTag(event.From) + containerName := "" + containerInfo, err := dockerClient.InspectContainer(containerId) + if err != nil { + logrus.Print("InspectContainer error:", err.Error()) + } else { + containerName = containerInfo.Name + } + data := url.Values{ + "action": {"stopContainer"}, + "id": {containerId}, + "name": {containerName}, + "imageRepo": {repo}, + "imageTag": {tag}} + CuberiteServerRequest(data) + case "destroy": + // logrus.Println("[goproxy] [event received] destroy") + // get container ID + containerId := event.ID + data := url.Values{ + "action": {"destroyContainer"}, + "id": {containerId}, + } + CuberiteServerRequest(data) } + // TODO: gdevillele: disable network events for now + case "network": + switch event.Action { + case "connect": + // a container has been connected to a network + + // id of the network + networkID := event.Actor.ID + // name of network + networkName := event.Actor.Attributes["name"] + // type of network + networkType := event.Actor.Attributes["type"] + // id of container affected + containerID := event.Actor.Attributes["container"] + + // TODO: gdevillele: clean this + if networkName == "bridge" || networkName == "none" || networkName == "host" { + // those are default network values + // we do nothing for now + return + } - data := url.Values{ - "action": {"stopContainer"}, - "id": {id}, - "name": {containerName}, - "imageRepo": {repo}, - "imageTag": {tag}} - - CuberiteServerRequest(data) - - case "destroy": - logrus.Debugln("destroy event") + logrus.Println("[goproxy] [event] ----- custom network connect -----") + logrus.Println("[goproxy] [event] | networkID:", networkID) + logrus.Println("[goproxy] [event] | networkName:", networkName) + logrus.Println("[goproxy] [event] | networkType:", networkType) + logrus.Println("[goproxy] [event] | containerID:", containerID) - data := url.Values{ - "action": {"destroyContainer"}, - "id": {id}, + // send a HTTP request to the Cuberite server + data := url.Values{ + "action": {"network_connect"}, + "networkId": {networkID}, + "networkName": {networkName}, + "networkType": {networkType}, + "containerId": {containerID}, + } + CuberiteServerRequest(data) } - - CuberiteServerRequest(data) } } @@ -188,6 +228,9 @@ func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{} // the cuberite server func statCallback(id string, stat *dockerclient.Stats, ec chan error, args ...interface{}) { + // TODO: gdevillele: re-activate stats later + return + // logrus.Debugln("STATS", id, stat) // logrus.Debugln("---") // logrus.Debugln("cpu :", float64(stat.CpuStats.CpuUsage.TotalUsage)/float64(stat.CpuStats.SystemUsage)) @@ -243,32 +286,28 @@ func execCmd(w http.ResponseWriter, r *http.Request) { // listContainers handles and reply to http requests having the path "/containers" func listContainers(w http.ResponseWriter, r *http.Request) { - // answer right away to avoid dead locks in LUA + // answer right away to avoid deadlocks in LUA io.WriteString(w, "OK") go func() { containers, err := dockerClient.ListContainers(true, false, "") - if err != nil { logrus.Println(err.Error()) return } images, err := dockerClient.ListImages(true) - if err != nil { logrus.Println(err.Error()) return } for i := 0; i < len(containers); i++ { - id := containers[i].Id info, _ := dockerClient.InspectContainer(id) name := info.Name[1:] imageRepo := "" imageTag := "" - for _, image := range images { if image.Id == info.Image { if len(image.RepoTags) > 0 { diff --git a/go/src/goproxy/vendor/github.com/Sirupsen/logrus/.travis.yml b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/.travis.yml new file mode 100644 index 00000000..ff23150d --- /dev/null +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - 1.3 + - 1.4 + - 1.5 + - tip +install: + - go get -t ./... +script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... diff --git a/go/src/github.com/Sirupsen/logrus/CHANGELOG.md b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/CHANGELOG.md similarity index 79% rename from go/src/github.com/Sirupsen/logrus/CHANGELOG.md rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/CHANGELOG.md index 53616dbc..9e9e6009 100644 --- a/go/src/github.com/Sirupsen/logrus/CHANGELOG.md +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/CHANGELOG.md @@ -1,8 +1,14 @@ -# 0.9.0 (Unreleased) +# 0.9.0 * logrus/text_formatter: don't emit empty msg * logrus/hooks/airbrake: move out of main repository * logrus/hooks/sentry: move out of main repository +* logrus/hooks/papertrail: move out of main repository +* logrus/hooks/bugsnag: move out of main repository +* logrus/core: run tests with `-race` +* logrus/core: detect TTY based on `stderr` +* logrus/core: support `WithError` on logger +* logrus/core: Solaris support # 0.8.7 diff --git a/go/src/github.com/Sirupsen/logrus/LICENSE b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/LICENSE similarity index 100% rename from go/src/github.com/Sirupsen/logrus/LICENSE rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/LICENSE diff --git a/go/src/github.com/Sirupsen/logrus/README.md b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/README.md similarity index 84% rename from go/src/github.com/Sirupsen/logrus/README.md rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/README.md index 3e526c1b..d3a1d4cf 100644 --- a/go/src/github.com/Sirupsen/logrus/README.md +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/README.md @@ -1,4 +1,4 @@ -# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc] +# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/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 @@ -202,9 +202,9 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | ----- | ----------- | | [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | | [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | -| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. | +| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. | | [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. | -| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | | [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. | | [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | | [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | @@ -218,6 +218,12 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | | [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | | [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | +| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | +| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit | +| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | +| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | +| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | +| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | #### Level logging @@ -298,12 +304,13 @@ The built-in logging formatters are: * `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events. ```go - logrus.SetFormatter(&logstash.LogstashFormatter{Type: “application_name"}) + logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"}) ``` Third party logging formatters: -* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. +* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. +* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a @@ -352,5 +359,27 @@ Log rotation is not provided with Logrus. Log rotation should be done by an external program (like `logrotate(8)`) that can compress and delete old log entries. It should not be a feature of the application-level logger. +#### Tools -[godoc]: https://godoc.org/github.com/Sirupsen/logrus +| Tool | Description | +| ---- | ----------- | +|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| + +#### Testing + +Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: + +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook +* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): + +```go +logger, hook := NewNullLogger() +logger.Error("Hello error") + +assert.Equal(1, len(hook.Entries)) +assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) +assert.Equal("Hello error", hook.LastEntry().Message) + +hook.Reset() +assert.Nil(hook.LastEntry()) +``` diff --git a/go/src/github.com/Sirupsen/logrus/doc.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/doc.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/doc.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/doc.go diff --git a/go/src/github.com/Sirupsen/logrus/entry.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/entry.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/entry.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/entry.go diff --git a/go/src/github.com/Sirupsen/logrus/entry_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/entry_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/entry_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/entry_test.go diff --git a/go/src/github.com/Sirupsen/logrus/examples/basic/basic.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/examples/basic/basic.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go diff --git a/go/src/github.com/Sirupsen/logrus/examples/hook/hook.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/examples/hook/hook.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go diff --git a/go/src/github.com/Sirupsen/logrus/exported.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/exported.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/exported.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/exported.go diff --git a/go/src/github.com/Sirupsen/logrus/formatter.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatter.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/formatter.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatter.go diff --git a/go/src/github.com/Sirupsen/logrus/formatter_bench_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/formatter_bench_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go diff --git a/go/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go similarity index 68% rename from go/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go index 8ea93ddf..aad646ab 100644 --- a/go/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go @@ -17,38 +17,43 @@ type LogstashFormatter struct { } func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { - entry.Data["@version"] = 1 + fields := make(logrus.Fields) + for k, v := range entry.Data { + fields[k] = v + } + + fields["@version"] = 1 if f.TimestampFormat == "" { f.TimestampFormat = logrus.DefaultTimestampFormat } - entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) + fields["@timestamp"] = entry.Time.Format(f.TimestampFormat) // set message field v, ok := entry.Data["message"] if ok { - entry.Data["fields.message"] = v + fields["fields.message"] = v } - entry.Data["message"] = entry.Message + fields["message"] = entry.Message // set level field v, ok = entry.Data["level"] if ok { - entry.Data["fields.level"] = v + fields["fields.level"] = v } - entry.Data["level"] = entry.Level.String() + fields["level"] = entry.Level.String() // set type field if f.Type != "" { v, ok = entry.Data["type"] if ok { - entry.Data["fields.type"] = v + fields["fields.type"] = v } - entry.Data["type"] = f.Type + fields["type"] = f.Type } - serialized, err := json.Marshal(entry.Data) + serialized, err := json.Marshal(fields) if err != nil { return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) } diff --git a/go/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go diff --git a/go/src/github.com/Sirupsen/logrus/hook_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hook_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/hook_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/hook_test.go diff --git a/go/src/github.com/Sirupsen/logrus/hooks.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/hooks.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks.go diff --git a/go/src/github.com/Sirupsen/logrus/hooks/syslog/README.md b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md similarity index 100% rename from go/src/github.com/Sirupsen/logrus/hooks/syslog/README.md rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md diff --git a/go/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go similarity index 89% rename from go/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go index b6fa3746..a36e2003 100644 --- a/go/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go @@ -1,3 +1,5 @@ +// +build !windows,!nacl,!plan9 + package logrus_syslog import ( @@ -48,12 +50,5 @@ func (hook *SyslogHook) Fire(entry *logrus.Entry) error { } func (hook *SyslogHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - logrus.WarnLevel, - logrus.InfoLevel, - logrus.DebugLevel, - } + return logrus.AllLevels } diff --git a/go/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go diff --git a/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/test/test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/test/test.go new file mode 100644 index 00000000..06881253 --- /dev/null +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/test/test.go @@ -0,0 +1,67 @@ +package test + +import ( + "io/ioutil" + + "github.com/Sirupsen/logrus" +) + +// test.Hook is a hook designed for dealing with logs in test scenarios. +type Hook struct { + Entries []*logrus.Entry +} + +// Installs a test hook for the global logger. +func NewGlobal() *Hook { + + hook := new(Hook) + logrus.AddHook(hook) + + return hook + +} + +// Installs a test hook for a given local logger. +func NewLocal(logger *logrus.Logger) *Hook { + + hook := new(Hook) + logger.Hooks.Add(hook) + + return hook + +} + +// Creates a discarding logger and installs the test hook. +func NewNullLogger() (*logrus.Logger, *Hook) { + + logger := logrus.New() + logger.Out = ioutil.Discard + + return logger, NewLocal(logger) + +} + +func (t *Hook) Fire(e *logrus.Entry) error { + t.Entries = append(t.Entries, e) + return nil +} + +func (t *Hook) Levels() []logrus.Level { + return logrus.AllLevels +} + +// LastEntry returns the last entry that was logged or nil. +func (t *Hook) LastEntry() (l *logrus.Entry) { + + if i := len(t.Entries) - 1; i < 0 { + return nil + } else { + return t.Entries[i] + } + +} + +// Reset removes all Entries from this test hook. +func (t *Hook) Reset() { + t.Entries = make([]*logrus.Entry, 0) +} diff --git a/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go new file mode 100644 index 00000000..d69455ba --- /dev/null +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go @@ -0,0 +1,39 @@ +package test + +import ( + "testing" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestAllHooks(t *testing.T) { + + assert := assert.New(t) + + logger, hook := NewNullLogger() + assert.Nil(hook.LastEntry()) + assert.Equal(0, len(hook.Entries)) + + logger.Error("Hello error") + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) + + logger.Warn("Hello warning") + assert.Equal(logrus.WarnLevel, hook.LastEntry().Level) + assert.Equal("Hello warning", hook.LastEntry().Message) + assert.Equal(2, len(hook.Entries)) + + hook.Reset() + assert.Nil(hook.LastEntry()) + assert.Equal(0, len(hook.Entries)) + + hook = NewGlobal() + + logrus.Error("Hello error") + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) + +} diff --git a/go/src/github.com/Sirupsen/logrus/json_formatter.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/json_formatter.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/json_formatter.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/json_formatter.go diff --git a/go/src/github.com/Sirupsen/logrus/json_formatter_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/json_formatter_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/json_formatter_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/json_formatter_test.go diff --git a/go/src/github.com/Sirupsen/logrus/logger.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/logger.go similarity index 96% rename from go/src/github.com/Sirupsen/logrus/logger.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/logger.go index fd9804c6..2fdb2317 100644 --- a/go/src/github.com/Sirupsen/logrus/logger.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/logger.go @@ -64,6 +64,12 @@ func (logger *Logger) WithFields(fields Fields) *Entry { return NewEntry(logger).WithFields(fields) } +// Add an error as single field to the log entry. All it does is call +// `WithError` for the given `error`. +func (logger *Logger) WithError(err error) *Entry { + return NewEntry(logger).WithError(err) +} + func (logger *Logger) Debugf(format string, args ...interface{}) { if logger.Level >= DebugLevel { NewEntry(logger).Debugf(format, args...) diff --git a/go/src/github.com/Sirupsen/logrus/logrus.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/logrus.go similarity index 92% rename from go/src/github.com/Sirupsen/logrus/logrus.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/logrus.go index 0c09fbc2..1e9670d8 100644 --- a/go/src/github.com/Sirupsen/logrus/logrus.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/logrus.go @@ -3,6 +3,7 @@ package logrus import ( "fmt" "log" + "strings" ) // Fields type, used to pass to `WithFields`. @@ -33,7 +34,7 @@ func (level Level) String() string { // ParseLevel takes a string level and returns the Logrus log level constant. func ParseLevel(lvl string) (Level, error) { - switch lvl { + switch strings.ToLower(lvl) { case "panic": return PanicLevel, nil case "fatal": @@ -52,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) { return l, fmt.Errorf("not a valid logrus Level: %q", lvl) } +// A constant exposing all logging levels +var AllLevels = []Level{ + PanicLevel, + FatalLevel, + ErrorLevel, + WarnLevel, + InfoLevel, + DebugLevel, +} + // These are the different logging levels. You can set the logging level to log // on your instance of logger, obtained with `logrus.New()`. const ( diff --git a/go/src/github.com/Sirupsen/logrus/logrus_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/logrus_test.go similarity index 90% rename from go/src/github.com/Sirupsen/logrus/logrus_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/logrus_test.go index efaacea2..2cf03f2f 100644 --- a/go/src/github.com/Sirupsen/logrus/logrus_test.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/logrus_test.go @@ -255,30 +255,58 @@ func TestParseLevel(t *testing.T) { assert.Nil(t, err) assert.Equal(t, PanicLevel, l) + 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("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("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("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("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("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("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()) } @@ -299,3 +327,18 @@ func TestGetSetLevelRace(t *testing.T) { } wg.Wait() } + +func TestLoggingRace(t *testing.T) { + logger := New() + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + logger.Info("info") + wg.Done() + }() + } + wg.Wait() +} diff --git a/go/src/github.com/Sirupsen/logrus/terminal_bsd.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_bsd.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/terminal_bsd.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_bsd.go diff --git a/go/src/github.com/Sirupsen/logrus/terminal_linux.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_linux.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/terminal_linux.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_linux.go diff --git a/go/src/github.com/Sirupsen/logrus/terminal_notwindows.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go similarity index 83% rename from go/src/github.com/Sirupsen/logrus/terminal_notwindows.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go index 4bb53760..b343b3a3 100644 --- a/go/src/github.com/Sirupsen/logrus/terminal_notwindows.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go @@ -12,9 +12,9 @@ import ( "unsafe" ) -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns true if stderr's file descriptor is a terminal. func IsTerminal() bool { - fd := syscall.Stdout + fd := syscall.Stderr 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/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_solaris.go new file mode 100644 index 00000000..3e70bf7b --- /dev/null +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_solaris.go @@ -0,0 +1,15 @@ +// +build solaris + +package logrus + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal() bool { + _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) + return err == nil +} diff --git a/go/src/github.com/Sirupsen/logrus/terminal_windows.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_windows.go similarity index 85% rename from go/src/github.com/Sirupsen/logrus/terminal_windows.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_windows.go index 2e09f6f7..0146845d 100644 --- a/go/src/github.com/Sirupsen/logrus/terminal_windows.go +++ b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/terminal_windows.go @@ -18,9 +18,9 @@ var ( procGetConsoleMode = kernel32.NewProc("GetConsoleMode") ) -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns true if stderr's file descriptor is a terminal. func IsTerminal() bool { - fd := syscall.Stdout + fd := syscall.Stderr 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/go/src/github.com/Sirupsen/logrus/text_formatter.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/text_formatter.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/text_formatter.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/text_formatter.go diff --git a/go/src/github.com/Sirupsen/logrus/text_formatter_test.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/text_formatter_test.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/text_formatter_test.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/text_formatter_test.go diff --git a/go/src/github.com/Sirupsen/logrus/writer.go b/go/src/goproxy/vendor/github.com/Sirupsen/logrus/writer.go similarity index 100% rename from go/src/github.com/Sirupsen/logrus/writer.go rename to go/src/goproxy/vendor/github.com/Sirupsen/logrus/writer.go diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/CONTRIBUTING.md b/go/src/goproxy/vendor/github.com/docker/go-units/CONTRIBUTING.md new file mode 100644 index 00000000..9ea86d78 --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to go-units + +Want to hack on go-units? Awesome! Here are instructions to get you started. + +go-units is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/LICENSE.code b/go/src/goproxy/vendor/github.com/docker/go-units/LICENSE.code new file mode 100644 index 00000000..b55b37bc --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/LICENSE.code @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/LICENSE.docs b/go/src/goproxy/vendor/github.com/docker/go-units/LICENSE.docs new file mode 100644 index 00000000..e26cd4fc --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/MAINTAINERS b/go/src/goproxy/vendor/github.com/docker/go-units/MAINTAINERS new file mode 100644 index 00000000..477be8b2 --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/MAINTAINERS @@ -0,0 +1,27 @@ +# go-connections maintainers file +# +# This file describes who runs the docker/go-connections project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "calavera", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + [people.calavera] + Name = "David Calavera" + Email = "david.calavera@gmail.com" + GitHub = "calavera" diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/README.md b/go/src/goproxy/vendor/github.com/docker/go-units/README.md new file mode 100644 index 00000000..3ce4d79d --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/README.md @@ -0,0 +1,18 @@ +[![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) + +# Introduction + +go-units is a library to transform human friendly measurements into machine friendly values. + +## Usage + +See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. + +## Copyright and license + +Copyright © 2015 Docker, Inc. All rights reserved, except as follows. Code +is released under the Apache 2.0 license. The README.md file, and files in the +"docs" folder are licensed under the Creative Commons Attribution 4.0 +International License under the terms and conditions set forth in the file +"LICENSE.docs". You may obtain a duplicate copy of the same license, titled +CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/circle.yml b/go/src/goproxy/vendor/github.com/docker/go-units/circle.yml new file mode 100644 index 00000000..9043b354 --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/circle.yml @@ -0,0 +1,11 @@ +dependencies: + post: + # install golint + - go get github.com/golang/lint/golint + +test: + pre: + # run analysis before tests + - go vet ./... + - test -z "$(golint ./... | tee /dev/stderr)" + - test -z "$(gofmt -s -l . | tee /dev/stderr)" diff --git a/go/src/github.com/docker/docker/pkg/units/duration.go b/go/src/goproxy/vendor/github.com/docker/go-units/duration.go old mode 100755 new mode 100644 similarity index 100% rename from go/src/github.com/docker/docker/pkg/units/duration.go rename to go/src/goproxy/vendor/github.com/docker/go-units/duration.go diff --git a/go/src/github.com/docker/docker/pkg/units/duration_test.go b/go/src/goproxy/vendor/github.com/docker/go-units/duration_test.go old mode 100755 new mode 100644 similarity index 54% rename from go/src/github.com/docker/docker/pkg/units/duration_test.go rename to go/src/goproxy/vendor/github.com/docker/go-units/duration_test.go index fcfb6b7b..63baa515 --- a/go/src/github.com/docker/docker/pkg/units/duration_test.go +++ b/go/src/goproxy/vendor/github.com/docker/go-units/duration_test.go @@ -1,10 +1,45 @@ package units import ( + "fmt" "testing" "time" ) +func ExampleHumanDuration() { + fmt.Println(HumanDuration(450 * time.Millisecond)) + fmt.Println(HumanDuration(47 * time.Second)) + fmt.Println(HumanDuration(1 * time.Minute)) + fmt.Println(HumanDuration(3 * time.Minute)) + fmt.Println(HumanDuration(35 * time.Minute)) + fmt.Println(HumanDuration(35*time.Minute + 40*time.Second)) + fmt.Println(HumanDuration(1 * time.Hour)) + fmt.Println(HumanDuration(1*time.Hour + 45*time.Minute)) + fmt.Println(HumanDuration(3 * time.Hour)) + fmt.Println(HumanDuration(3*time.Hour + 59*time.Minute)) + fmt.Println(HumanDuration(3*time.Hour + 60*time.Minute)) + fmt.Println(HumanDuration(24 * time.Hour)) + fmt.Println(HumanDuration(24*time.Hour + 12*time.Hour)) + fmt.Println(HumanDuration(2 * 24 * time.Hour)) + fmt.Println(HumanDuration(7 * 24 * time.Hour)) + fmt.Println(HumanDuration(13*24*time.Hour + 5*time.Hour)) + fmt.Println(HumanDuration(2 * 7 * 24 * time.Hour)) + fmt.Println(HumanDuration(2*7*24*time.Hour + 4*24*time.Hour)) + fmt.Println(HumanDuration(3 * 7 * 24 * time.Hour)) + fmt.Println(HumanDuration(4 * 7 * 24 * time.Hour)) + fmt.Println(HumanDuration(4*7*24*time.Hour + 3*24*time.Hour)) + fmt.Println(HumanDuration(1 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(1*30*24*time.Hour + 2*7*24*time.Hour)) + fmt.Println(HumanDuration(2 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(3*30*24*time.Hour + 1*7*24*time.Hour)) + fmt.Println(HumanDuration(5*30*24*time.Hour + 2*7*24*time.Hour)) + fmt.Println(HumanDuration(13 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(23 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(24 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(24*30*24*time.Hour + 2*7*24*time.Hour)) + fmt.Println(HumanDuration(3*365*24*time.Hour + 2*30*24*time.Hour)) +} + func TestHumanDuration(t *testing.T) { // Useful duration abstractions day := 24 * time.Hour diff --git a/go/src/github.com/docker/docker/pkg/units/size.go b/go/src/goproxy/vendor/github.com/docker/go-units/size.go old mode 100755 new mode 100644 similarity index 90% rename from go/src/github.com/docker/docker/pkg/units/size.go rename to go/src/goproxy/vendor/github.com/docker/go-units/size.go index 3b59daff..989edd29 --- a/go/src/github.com/docker/docker/pkg/units/size.go +++ b/go/src/goproxy/vendor/github.com/docker/go-units/size.go @@ -31,7 +31,7 @@ type unitMap map[string]int64 var ( decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} - sizeRegex = regexp.MustCompile(`^(\d+)([kKmMgGtTpP])?[bB]?$`) + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`) ) var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} @@ -77,19 +77,19 @@ func RAMInBytes(size string) (int64, error) { // Parses the human-readable size string into the amount it represents. func parseSize(sizeStr string, uMap unitMap) (int64, error) { matches := sizeRegex.FindStringSubmatch(sizeStr) - if len(matches) != 3 { + if len(matches) != 4 { return -1, fmt.Errorf("invalid size: '%s'", sizeStr) } - size, err := strconv.ParseInt(matches[1], 10, 0) + size, err := strconv.ParseFloat(matches[1], 64) if err != nil { return -1, err } - unitPrefix := strings.ToLower(matches[2]) + unitPrefix := strings.ToLower(matches[3]) if mul, ok := uMap[unitPrefix]; ok { - size *= mul + size *= float64(mul) } - return size, nil + return int64(size), nil } diff --git a/go/src/github.com/docker/docker/pkg/units/size_test.go b/go/src/goproxy/vendor/github.com/docker/go-units/size_test.go old mode 100755 new mode 100644 similarity index 66% rename from go/src/github.com/docker/docker/pkg/units/size_test.go rename to go/src/goproxy/vendor/github.com/docker/go-units/size_test.go index 67c3b81e..003bc89a --- a/go/src/github.com/docker/docker/pkg/units/size_test.go +++ b/go/src/goproxy/vendor/github.com/docker/go-units/size_test.go @@ -1,12 +1,64 @@ package units import ( + "fmt" "reflect" "runtime" "strings" "testing" ) +func ExampleBytesSize() { + fmt.Println(BytesSize(1024)) + fmt.Println(BytesSize(1024 * 1024)) + fmt.Println(BytesSize(1048576)) + fmt.Println(BytesSize(2 * MiB)) + fmt.Println(BytesSize(3.42 * GiB)) + fmt.Println(BytesSize(5.372 * TiB)) + fmt.Println(BytesSize(2.22 * PiB)) +} + +func ExampleHumanSize() { + fmt.Println(HumanSize(1000)) + fmt.Println(HumanSize(1024)) + fmt.Println(HumanSize(1000000)) + fmt.Println(HumanSize(1048576)) + fmt.Println(HumanSize(2 * MB)) + fmt.Println(HumanSize(float64(3.42 * GB))) + fmt.Println(HumanSize(float64(5.372 * TB))) + fmt.Println(HumanSize(float64(2.22 * PB))) +} + +func ExampleFromHumanSize() { + fmt.Println(FromHumanSize("32")) + fmt.Println(FromHumanSize("32b")) + fmt.Println(FromHumanSize("32B")) + fmt.Println(FromHumanSize("32k")) + fmt.Println(FromHumanSize("32K")) + fmt.Println(FromHumanSize("32kb")) + fmt.Println(FromHumanSize("32Kb")) + fmt.Println(FromHumanSize("32Mb")) + fmt.Println(FromHumanSize("32Gb")) + fmt.Println(FromHumanSize("32Tb")) + fmt.Println(FromHumanSize("32Pb")) +} + +func ExampleRAMInBytes() { + fmt.Println(RAMInBytes("32")) + fmt.Println(RAMInBytes("32b")) + fmt.Println(RAMInBytes("32B")) + fmt.Println(RAMInBytes("32k")) + fmt.Println(RAMInBytes("32K")) + fmt.Println(RAMInBytes("32kb")) + fmt.Println(RAMInBytes("32Kb")) + fmt.Println(RAMInBytes("32Mb")) + fmt.Println(RAMInBytes("32Gb")) + fmt.Println(RAMInBytes("32Tb")) + fmt.Println(RAMInBytes("32Pb")) + fmt.Println(RAMInBytes("32PB")) + fmt.Println(RAMInBytes("32P")) +} + func TestBytesSize(t *testing.T) { assertEquals(t, "1 KiB", BytesSize(1024)) assertEquals(t, "1 MiB", BytesSize(1024*1024)) @@ -41,13 +93,15 @@ func TestFromHumanSize(t *testing.T) { assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb") assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb") + assertSuccessEquals(t, 32.5*KB, FromHumanSize, "32.5kB") + assertSuccessEquals(t, 32.5*KB, FromHumanSize, "32.5 kB") + assertSuccessEquals(t, 32, FromHumanSize, "32.5 B") + assertError(t, FromHumanSize, "") assertError(t, FromHumanSize, "hello") assertError(t, FromHumanSize, "-32") - assertError(t, FromHumanSize, "32.3") + assertError(t, FromHumanSize, ".3kB") assertError(t, FromHumanSize, " 32 ") - assertError(t, FromHumanSize, "32.3Kb") - assertError(t, FromHumanSize, "32 mb") assertError(t, FromHumanSize, "32m b") assertError(t, FromHumanSize, "32bm") } @@ -67,13 +121,14 @@ func TestRAMInBytes(t *testing.T) { assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB") assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P") + assertSuccessEquals(t, 32, RAMInBytes, "32.3") + tmp := 32.3 * MiB + assertSuccessEquals(t, int64(tmp), RAMInBytes, "32.3 mb") + assertError(t, RAMInBytes, "") assertError(t, RAMInBytes, "hello") assertError(t, RAMInBytes, "-32") - assertError(t, RAMInBytes, "32.3") assertError(t, RAMInBytes, " 32 ") - assertError(t, RAMInBytes, "32.3Kb") - assertError(t, RAMInBytes, "32 mb") assertError(t, RAMInBytes, "32m b") assertError(t, RAMInBytes, "32bm") } diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/ulimit.go b/go/src/goproxy/vendor/github.com/docker/go-units/ulimit.go new file mode 100644 index 00000000..5ac7fd82 --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/ulimit.go @@ -0,0 +1,118 @@ +package units + +import ( + "fmt" + "strconv" + "strings" +) + +// Ulimit is a human friendly version of Rlimit. +type Ulimit struct { + Name string + Hard int64 + Soft int64 +} + +// Rlimit specifies the resource limits, such as max open files. +type Rlimit struct { + Type int `json:"type,omitempty"` + Hard uint64 `json:"hard,omitempty"` + Soft uint64 `json:"soft,omitempty"` +} + +const ( + // magic numbers for making the syscall + // some of these are defined in the syscall package, but not all. + // Also since Windows client doesn't get access to the syscall package, need to + // define these here + rlimitAs = 9 + rlimitCore = 4 + rlimitCPU = 0 + rlimitData = 2 + rlimitFsize = 1 + rlimitLocks = 10 + rlimitMemlock = 8 + rlimitMsgqueue = 12 + rlimitNice = 13 + rlimitNofile = 7 + rlimitNproc = 6 + rlimitRss = 5 + rlimitRtprio = 14 + rlimitRttime = 15 + rlimitSigpending = 11 + rlimitStack = 3 +) + +var ulimitNameMapping = map[string]int{ + //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. + "core": rlimitCore, + "cpu": rlimitCPU, + "data": rlimitData, + "fsize": rlimitFsize, + "locks": rlimitLocks, + "memlock": rlimitMemlock, + "msgqueue": rlimitMsgqueue, + "nice": rlimitNice, + "nofile": rlimitNofile, + "nproc": rlimitNproc, + "rss": rlimitRss, + "rtprio": rlimitRtprio, + "rttime": rlimitRttime, + "sigpending": rlimitSigpending, + "stack": rlimitStack, +} + +// ParseUlimit parses and returns a Ulimit from the specified string. +func ParseUlimit(val string) (*Ulimit, error) { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid ulimit argument: %s", val) + } + + if _, exists := ulimitNameMapping[parts[0]]; !exists { + return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) + } + + var ( + soft int64 + hard = &soft // default to soft in case no hard was set + temp int64 + err error + ) + switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { + case 2: + temp, err = strconv.ParseInt(limitVals[1], 10, 64) + if err != nil { + return nil, err + } + hard = &temp + fallthrough + case 1: + soft, err = strconv.ParseInt(limitVals[0], 10, 64) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) + } + + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } + + return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil +} + +// GetRlimit returns the RLimit corresponding to Ulimit. +func (u *Ulimit) GetRlimit() (*Rlimit, error) { + t, exists := ulimitNameMapping[u.Name] + if !exists { + return nil, fmt.Errorf("invalid ulimit name %s", u.Name) + } + + return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil +} + +func (u *Ulimit) String() string { + return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) +} diff --git a/go/src/goproxy/vendor/github.com/docker/go-units/ulimit_test.go b/go/src/goproxy/vendor/github.com/docker/go-units/ulimit_test.go new file mode 100644 index 00000000..3e7f10fc --- /dev/null +++ b/go/src/goproxy/vendor/github.com/docker/go-units/ulimit_test.go @@ -0,0 +1,74 @@ +package units + +import ( + "fmt" + "strconv" + "testing" +) + +func ExampleParseUlimit() { + fmt.Println(ParseUlimit("nofile=512:1024")) + fmt.Println(ParseUlimit("nofile=1024")) + fmt.Println(ParseUlimit("cpu=2:4")) + fmt.Println(ParseUlimit("cpu=6")) +} + +func TestParseUlimitValid(t *testing.T) { + u1 := &Ulimit{"nofile", 1024, 512} + if u2, _ := ParseUlimit("nofile=512:1024"); *u1 != *u2 { + t.Fatalf("expected %q, but got %q", u1, u2) + } +} + +func TestParseUlimitInvalidLimitType(t *testing.T) { + if _, err := ParseUlimit("notarealtype=1024:1024"); err == nil { + t.Fatalf("expected error on invalid ulimit type") + } +} + +func TestParseUlimitBadFormat(t *testing.T) { + if _, err := ParseUlimit("nofile:1024:1024"); err == nil { + t.Fatal("expected error on bad syntax") + } + + if _, err := ParseUlimit("nofile"); err == nil { + t.Fatal("expected error on bad syntax") + } + + if _, err := ParseUlimit("nofile="); err == nil { + t.Fatal("expected error on bad syntax") + } + if _, err := ParseUlimit("nofile=:"); err == nil { + t.Fatal("expected error on bad syntax") + } + if _, err := ParseUlimit("nofile=:1024"); err == nil { + t.Fatal("expected error on bad syntax") + } +} + +func TestParseUlimitHardLessThanSoft(t *testing.T) { + if _, err := ParseUlimit("nofile=1024:1"); err == nil { + t.Fatal("expected error on hard limit less than soft limit") + } +} + +func TestParseUlimitInvalidValueType(t *testing.T) { + if _, err := ParseUlimit("nofile=asdf"); err == nil { + t.Fatal("expected error on bad value type, but got no error") + } else if _, ok := err.(*strconv.NumError); !ok { + t.Fatalf("expected error on bad value type, but got `%s`", err) + } + + if _, err := ParseUlimit("nofile=1024:asdf"); err == nil { + t.Fatal("expected error on bad value type, but got no error") + } else if _, ok := err.(*strconv.NumError); !ok { + t.Fatalf("expected error on bad value type, but got `%s`", err) + } +} + +func TestUlimitStringOutput(t *testing.T) { + u := &Ulimit{"nofile", 1024, 512} + if s := u.String(); s != "nofile=512:1024" { + t.Fatal("expected String to return nofile=512:1024, but got", s) + } +} diff --git a/go/src/github.com/samalba/dockerclient/LICENSE b/go/src/goproxy/vendor/github.com/samalba/dockerclient/LICENSE old mode 100755 new mode 100644 similarity index 100% rename from go/src/github.com/samalba/dockerclient/LICENSE rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/LICENSE diff --git a/go/src/github.com/samalba/dockerclient/README.md b/go/src/goproxy/vendor/github.com/samalba/dockerclient/README.md old mode 100755 new mode 100644 similarity index 99% rename from go/src/github.com/samalba/dockerclient/README.md rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/README.md index 5a5027b8..6cad9bd3 --- a/go/src/github.com/samalba/dockerclient/README.md +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/README.md @@ -63,7 +63,7 @@ func main() { Cmd: []string{"bash"}, AttachStdin: true, Tty: true} - containerId, err := docker.CreateContainer(containerConfig, "foobar") + containerId, err := docker.CreateContainer(containerConfig, "foobar", nil) if err != nil { log.Fatal(err) } diff --git a/go/src/github.com/samalba/dockerclient/auth.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/auth.go old mode 100755 new mode 100644 similarity index 82% rename from go/src/github.com/samalba/dockerclient/auth.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/auth.go index 48f5f90b..7c0a67f7 --- a/go/src/github.com/samalba/dockerclient/auth.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/auth.go @@ -8,9 +8,10 @@ import ( // AuthConfig hold parameters for authenticating with the docker registry type AuthConfig struct { - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - Email string `json:"email,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Email string `json:"email,omitempty"` + RegistryToken string `json:"registrytoken,omitempty"` } // encode the auth configuration struct into base64 for the X-Registry-Auth header diff --git a/go/src/github.com/samalba/dockerclient/auth_test.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/auth_test.go similarity index 100% rename from go/src/github.com/samalba/dockerclient/auth_test.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/auth_test.go diff --git a/go/src/github.com/samalba/dockerclient/dockerclient.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/dockerclient.go old mode 100755 new mode 100644 similarity index 86% rename from go/src/github.com/samalba/dockerclient/dockerclient.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/dockerclient.go index 96ea37fe..51a00b29 --- a/go/src/github.com/samalba/dockerclient/dockerclient.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/dockerclient.go @@ -16,13 +16,23 @@ import ( "time" ) +var _ Client = (*DockerClient)(nil) + const ( + // APIVersion is currently hardcoded to v1.15 + // TODO: bump the API version or allow users to choose which API version to + // use the client with. The current value does not make sense for many + // methods, such as ContainerStats, StartMonitorStats, and StopAllMonitorStats + // (v1.17) and + // ListVolumes, {Remove,Create}Volume, ListNetworks, + // {Inspect,Create,Connect,Disconnect,Remove}Network (v1.21) APIVersion = "v1.15" ) var ( - ErrImageNotFound = errors.New("Image not found") - ErrNotFound = errors.New("Not found") + ErrImageNotFound = errors.New("Image not found") + ErrNotFound = errors.New("Not found") + ErrConnectionRefused = errors.New("Cannot connect to the docker engine endpoint") defaultTimeout = 30 * time.Second ) @@ -46,10 +56,10 @@ func (e Error) Error() string { } func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) { - return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout)) + return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout), nil) } -func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration) (*DockerClient, error) { +func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration, setUserTimeout tcpFunc) (*DockerClient, error) { u, err := url.Parse(daemonUrl) if err != nil { return nil, err @@ -61,7 +71,7 @@ func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout tim u.Scheme = "https" } } - httpClient := newHTTPClient(u, tlsConfig, timeout) + httpClient := newHTTPClient(u, tlsConfig, timeout, setUserTimeout) return &DockerClient{u, httpClient, tlsConfig, 0, nil}, nil } @@ -100,6 +110,9 @@ func (client *DockerClient) doStreamRequest(method string, path string, in io.Re if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil { return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err) } + if strings.Contains(err.Error(), "connection refused") { + return nil, ErrConnectionRefused + } return nil, err } if resp.StatusCode == 404 { @@ -184,7 +197,7 @@ func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) return info, nil } -func (client *DockerClient) CreateContainer(config *ContainerConfig, name string) (string, error) { +func (client *DockerClient) CreateContainer(config *ContainerConfig, name string, auth *AuthConfig) (string, error) { data, err := json.Marshal(config) if err != nil { return "", err @@ -195,14 +208,22 @@ func (client *DockerClient) CreateContainer(config *ContainerConfig, name string v.Set("name", name) uri = fmt.Sprintf("%s?%s", uri, v.Encode()) } - data, err = client.doRequest("POST", uri, data, nil) + headers := map[string]string{} + if auth != nil { + encoded_auth, err := auth.encode() + if err != nil { + return "", err + } + headers["X-Registry-Auth"] = encoded_auth + } + data, err = client.doRequest("POST", uri, data, headers) if err != nil { return "", err } result := &RespContainersCreate{} err = json.Unmarshal(data, result) if err != nil { - return "", err + return "", fmt.Errorf(string(data)) } return result.Id, nil } @@ -244,6 +265,36 @@ func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, er return changes, nil } +func (client *DockerClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error) { + uri := fmt.Sprintf("/%s/containers/%s/stats", APIVersion, id) + resp, err := client.HTTPClient.Get(client.URL.String() + uri) + if err != nil { + return nil, err + } + + decode := func(decoder *json.Decoder) decodingResult { + var containerStats Stats + if err := decoder.Decode(&containerStats); err != nil { + return decodingResult{err: err} + } else { + return decodingResult{result: containerStats} + } + } + decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan) + statsOrErrorChan := make(chan StatsOrError) + go func() { + for decodingResult := range decodingResultChan { + stats, _ := decodingResult.result.(Stats) + statsOrErrorChan <- StatsOrError{ + Stats: stats, + Error: decodingResult.err, + } + } + close(statsOrErrorChan) + }() + return statsOrErrorChan, nil +} + func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult { resultChan := make(chan decodingResult) @@ -335,6 +386,29 @@ func (client *DockerClient) ExecResize(id string, width, height int) error { return nil } +func (client *DockerClient) AttachContainer(id string, options *AttachOptions) (io.ReadCloser, error) { + v := url.Values{} + if options != nil { + if options.Logs { + v.Set("logs", "1") + } + if options.Stream { + v.Set("stream", "1") + } + if options.Stdin { + v.Set("stdin", "1") + } + if options.Stdout { + v.Set("stdout", "1") + } + if options.Stderr { + v.Set("stderr", "1") + } + } + uri := fmt.Sprintf("/%s/containers/%s/attach?%s", APIVersion, id, v.Encode()) + return client.doStreamRequest("POST", uri, nil, nil) +} + func (client *DockerClient) StartContainer(id string, config *HostConfig) error { data, err := json.Marshal(config) if err != nil { @@ -468,7 +542,7 @@ func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args for e := range eventErrChan { if e.Error != nil { if ec != nil { - ec <- err + ec <- e.Error } return } @@ -478,6 +552,9 @@ func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args } func (client *DockerClient) StopAllMonitorEvents() { + if client.eventStopChan == nil { + return + } close(client.eventStopChan) } @@ -625,17 +702,9 @@ func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) { } func (client *DockerClient) LoadImage(reader io.Reader) error { - data, err := ioutil.ReadAll(reader) - if err != nil { - return err - } - uri := fmt.Sprintf("/%s/images/load", APIVersion) - _, err = client.doRequest("POST", uri, data, nil) - if err != nil { - return err - } - return nil + _, err := client.doStreamRequest("POST", uri, reader, nil) + return err } func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error { @@ -689,6 +758,31 @@ func (client *DockerClient) RemoveImage(name string, force bool) ([]*ImageDelete return imageDelete, nil } +func (client *DockerClient) SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error) { + term := query + if registry != "" { + term = registry + "/" + term + } + uri := fmt.Sprintf("/%s/images/search?term=%s", APIVersion, term) + headers := map[string]string{} + if auth != nil { + if encodedAuth, err := auth.encode(); err != nil { + return nil, err + } else { + headers["X-Registry-Auth"] = encodedAuth + } + } + data, err := client.doRequest("GET", uri, nil, headers) + if err != nil { + return nil, err + } + var imageSearches []ImageSearch + if err := json.Unmarshal(data, &imageSearches); err != nil { + return nil, err + } + return imageSearches, nil +} + func (client *DockerClient) PauseContainer(id string) error { uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id) _, err := client.doRequest("POST", uri, nil, nil) @@ -890,8 +984,8 @@ func (client *DockerClient) ConnectNetwork(id, container string) error { return err } -func (client *DockerClient) DisconnectNetwork(id, container string) error { - data, err := json.Marshal(NetworkDisconnect{Container: container}) +func (client *DockerClient) DisconnectNetwork(id, container string, force bool) error { + data, err := json.Marshal(NetworkDisconnect{Container: container, Force: force}) if err != nil { return err } diff --git a/go/src/github.com/samalba/dockerclient/dockerclient_test.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/dockerclient_test.go old mode 100755 new mode 100644 similarity index 73% rename from go/src/github.com/samalba/dockerclient/dockerclient_test.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/dockerclient_test.go index afc1b071..7c7b3f59 --- a/go/src/github.com/samalba/dockerclient/dockerclient_test.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/dockerclient_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "os" "reflect" "strings" "testing" @@ -31,6 +32,40 @@ func testDockerClient(t *testing.T) *DockerClient { return client } +func ExampleDockerClient_AttachContainer() { + docker, err := NewDockerClient("unix:///var/run/docker.sock", nil) + if err != nil { + panic(err) + } + cID, err := docker.CreateContainer(&ContainerConfig{ + Cmd: []string{"echo", "hi"}, + Image: "busybox", + }, "", nil) + if err != nil { + panic(err) + } + done := make(chan struct{}) + if body, err := docker.AttachContainer(cID, &AttachOptions{ + Stream: true, + Stdout: true, + }); err != nil { + panic(err) + } else { + go func() { + defer body.Close() + if _, err := stdcopy.StdCopy(os.Stdout, os.Stderr, body); err != nil { + panic(err) + } + close(done) + }() + } + + if err := docker.StartContainer(cID, nil); err != nil { + panic(err) + } + <-done +} + func TestInfo(t *testing.T) { client := testDockerClient(t) info, err := client.Info() @@ -179,6 +214,50 @@ func TestContainerLogs(t *testing.T) { } } +func TestContainerStats(t *testing.T) { + client := testDockerClient(t) + var expectedContainerStats Stats + if err := json.Unmarshal([]byte(statsResp), &expectedContainerStats); err != nil { + t.Fatalf("cannot parse expected resp: %s", err.Error()) + } + containerIds := []string{"foobar", "foo"} + expectedResults := [][]StatsOrError{ + {{Stats: expectedContainerStats}, {Error: fmt.Errorf("invalid character 'i' looking for beginning of value")}}, + {{Stats: expectedContainerStats}, {Stats: expectedContainerStats}}, + } + + for i := range containerIds { + t.Logf("on outer iter %d\n", i) + stopChan := make(chan struct{}) + statsOrErrorChan, err := client.ContainerStats(containerIds[i], stopChan) + if err != nil { + t.Fatalf("cannot get stats from server: %s", err.Error()) + } + + for j, expectedResult := range expectedResults[i] { + t.Logf("on iter %d\n", j) + containerStatsOrError := <-statsOrErrorChan + if containerStatsOrError.Error != nil { + if expectedResult.Error == nil { + t.Fatalf("index %d, got unexpected error %v", j, containerStatsOrError.Error) + } else if containerStatsOrError.Error.Error() == expectedResult.Error.Error() { + // continue so that we don't try to + // compare error values directly + continue + } else { + t.Fatalf("index %d, expected error %q but got %q", j, expectedResult.Error, containerStatsOrError.Error) + } + } + if !reflect.DeepEqual(containerStatsOrError, expectedResult) { + t.Fatalf("index %d, got:\n%#v\nexpected:\n%#v", j, containerStatsOrError, expectedResult) + } + t.Logf("done with iter %d\n", j) + } + close(stopChan) + t.Logf("done with outer iter %d\n", i) + } +} + func TestMonitorEvents(t *testing.T) { client := testDockerClient(t) decoder := json.NewDecoder(bytes.NewBufferString(eventsResp)) diff --git a/go/src/github.com/samalba/dockerclient/engine_mock_test.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/engine_mock_test.go old mode 100755 new mode 100644 similarity index 94% rename from go/src/github.com/samalba/dockerclient/engine_mock_test.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/engine_mock_test.go index 7d3a6d93..b2382a15 --- a/go/src/github.com/samalba/dockerclient/engine_mock_test.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/engine_mock_test.go @@ -13,7 +13,6 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/jsonlog" "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/timeutils" "github.com/gorilla/mux" ) @@ -28,6 +27,7 @@ func init() { r.HandleFunc(baseURL+"/containers/json", handlerGetContainers).Methods("GET") r.HandleFunc(baseURL+"/containers/{id}/logs", handleContainerLogs).Methods("GET") r.HandleFunc(baseURL+"/containers/{id}/changes", handleContainerChanges).Methods("GET") + r.HandleFunc(baseURL+"/containers/{id}/stats", handleContainerStats).Methods("GET") r.HandleFunc(baseURL+"/containers/{id}/kill", handleContainerKill).Methods("POST") r.HandleFunc(baseURL+"/containers/{id}/wait", handleWait).Methods("POST") r.HandleFunc(baseURL+"/images/create", handleImagePull).Methods("POST") @@ -109,7 +109,7 @@ func handleContainerLogs(w http.ResponseWriter, r *http.Request) { line := fmt.Sprintf("line %d", i) if getBoolValue(r.Form.Get("timestamps")) { l := &jsonlog.JSONLog{Log: line, Created: time.Now().UTC()} - line = fmt.Sprintf("%s %s", l.Created.Format(timeutils.RFC3339NanoFixed), line) + line = fmt.Sprintf("%s %s", l.Created.Format(jsonlog.RFC3339NanoFixed), line) } if i%2 == 0 && stderr { fmt.Fprintln(errStream, line) @@ -138,6 +138,15 @@ func handleContainerChanges(w http.ResponseWriter, r *http.Request) { w.Write([]byte(body)) } +func handleContainerStats(w http.ResponseWriter, r *http.Request) { + switch mux.Vars(r)["id"] { + case "foobar": + fmt.Fprintf(w, "%s invalidresp", statsResp) + default: + fmt.Fprintf(w, "%s %s", statsResp, statsResp) + } +} + func getBoolValue(boolString string) bool { switch boolString { case "1": diff --git a/go/src/github.com/samalba/dockerclient/example_responses.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/example_responses.go old mode 100755 new mode 100644 similarity index 66% rename from go/src/github.com/samalba/dockerclient/example_responses.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/example_responses.go index 670508c0..e4c890dc --- a/go/src/github.com/samalba/dockerclient/example_responses.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/example_responses.go @@ -10,4 +10,6 @@ var haproxyPullOutput = `{"status":"The image you are pulling has been verified" {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"Status: Image is up to date for haproxy"} ` +var statsResp = `{"read":"2015-02-02T17:06:08.187833376-05:00","network":{"rx_bytes":99988,"rx_packets":928,"rx_errors":0,"rx_dropped":0,"tx_bytes":1786548,"tx_packets":877,"tx_errors":0,"tx_dropped":0},"cpu_stats":{"cpu_usage":{"total_usage":170018598,"percpu_usage":[170018598],"usage_in_kernelmode":30000000,"usage_in_usermode":70000000},"system_cpu_usage":9020930000000,"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"memory_stats":{"usage":18022400,"max_usage":20541440,"stats":{"active_anon":6213632,"active_file":176128,"cache":11808768,"hierarchical_memory_limit":9223372036854775807,"hierarchical_memsw_limit":9223372036854775807,"inactive_anon":0,"inactive_file":11632640,"mapped_file":5165056,"pgfault":2535,"pgmajfault":13,"pgpgin":4293,"pgpgout":1937,"rss":6213632,"rss_huge":2097152,"swap":0,"total_active_anon":6213632,"total_active_file":176128,"total_cache":11808768,"total_inactive_anon":0,"total_inactive_file":11632640,"total_mapped_file":5165056,"total_pgfault":2535,"total_pgmajfault":13,"total_pgpgin":4293,"total_pgpgout":1937,"total_rss":6213632,"total_rss_huge":2097152,"total_swap":0,"total_unevictable":0,"unevictable":0},"failcnt":0,"limit":1041051648},"blkio_stats":{"io_service_bytes_recursive":[{"major":7,"minor":0,"op":"Read","value":28672},{"major":7,"minor":0,"op":"Write","value":0},{"major":7,"minor":0,"op":"Sync","value":0},{"major":7,"minor":0,"op":"Async","value":28672},{"major":7,"minor":0,"op":"Total","value":28672},{"major":253,"minor":0,"op":"Read","value":28672},{"major":253,"minor":0,"op":"Write","value":0},{"major":253,"minor":0,"op":"Sync","value":0},{"major":253,"minor":0,"op":"Async","value":28672},{"major":253,"minor":0,"op":"Total","value":28672},{"major":253,"minor":7,"op":"Read","value":11718656},{"major":253,"minor":7,"op":"Write","value":0},{"major":253,"minor":7,"op":"Sync","value":0},{"major":253,"minor":7,"op":"Async","value":11718656},{"major":253,"minor":7,"op":"Total","value":11718656},{"major":202,"minor":0,"op":"Read","value":0},{"major":202,"minor":0,"op":"Write","value":0},{"major":202,"minor":0,"op":"Sync","value":0},{"major":202,"minor":0,"op":"Async","value":0},{"major":202,"minor":0,"op":"Total","value":0}],"io_serviced_recursive":[{"major":7,"minor":0,"op":"Read","value":7},{"major":7,"minor":0,"op":"Write","value":0},{"major":7,"minor":0,"op":"Sync","value":0},{"major":7,"minor":0,"op":"Async","value":7},{"major":7,"minor":0,"op":"Total","value":7},{"major":253,"minor":0,"op":"Read","value":7},{"major":253,"minor":0,"op":"Write","value":0},{"major":253,"minor":0,"op":"Sync","value":0},{"major":253,"minor":0,"op":"Async","value":7},{"major":253,"minor":0,"op":"Total","value":7},{"major":253,"minor":7,"op":"Read","value":312},{"major":253,"minor":7,"op":"Write","value":0},{"major":253,"minor":7,"op":"Sync","value":0},{"major":253,"minor":7,"op":"Async","value":312},{"major":253,"minor":7,"op":"Total","value":312},{"major":202,"minor":0,"op":"Read","value":0},{"major":202,"minor":0,"op":"Write","value":0},{"major":202,"minor":0,"op":"Sync","value":0},{"major":202,"minor":0,"op":"Async","value":0},{"major":202,"minor":0,"op":"Total","value":0}],"io_queue_recursive":[],"io_service_time_recursive":[],"io_wait_time_recursive":[],"io_merged_recursive":[],"io_time_recursive":[],"sectors_recursive":[]}}` + var eventsResp = `{"status":"pull","id":"nginx:latest","time":1428620433}{"status":"create","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"start","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"die","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620442}{"status":"create","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"start","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"die","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"pull","id":"debian:latest","time":1428620453}{"status":"create","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"start","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"die","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"create","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"start","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"pause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620462}{"status":"unpause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620466}{"status":"die","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620469}` diff --git a/go/src/github.com/samalba/dockerclient/examples/events.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/examples/events.go old mode 100755 new mode 100644 similarity index 100% rename from go/src/github.com/samalba/dockerclient/examples/events.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/examples/events.go diff --git a/go/src/github.com/samalba/dockerclient/examples/stats/stats.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/examples/stats/stats.go old mode 100755 new mode 100644 similarity index 92% rename from go/src/github.com/samalba/dockerclient/examples/stats/stats.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/examples/stats/stats.go index 9027069d..81ee2fb4 --- a/go/src/github.com/samalba/dockerclient/examples/stats/stats.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/examples/stats/stats.go @@ -27,7 +27,7 @@ func main() { } containerConfig := &dockerclient.ContainerConfig{Image: "busybox", Cmd: []string{"sh"}} - containerId, err := docker.CreateContainer(containerConfig, "") + containerId, err := docker.CreateContainer(containerConfig, "", nil) if err != nil { log.Fatal(err) } diff --git a/go/src/github.com/samalba/dockerclient/interface.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/interface.go old mode 100755 new mode 100644 similarity index 78% rename from go/src/github.com/samalba/dockerclient/interface.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/interface.go index cb7a8b79..b373055e --- a/go/src/github.com/samalba/dockerclient/interface.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/interface.go @@ -13,13 +13,20 @@ type Client interface { ListContainers(all, size bool, filters string) ([]Container, error) InspectContainer(id string) (*ContainerInfo, error) InspectImage(id string) (*ImageInfo, error) - CreateContainer(config *ContainerConfig, name string) (string, error) + CreateContainer(config *ContainerConfig, name string, authConfig *AuthConfig) (string, error) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) ContainerChanges(id string) ([]*ContainerChanges, error) + // ContainerStats takes a container ID and an optional stop channel and + // returns a StatsOrError channel. If an error is ever sent, then no + // more stats will be sent on that channel. If a stop channel is + // provided, events will stop being monitored after the stop channel is + // closed. + ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error) ExecCreate(config *ExecConfig) (string, error) ExecStart(id string, config *ExecConfig) error ExecResize(id string, width, height int) error StartContainer(id string, config *HostConfig) error + AttachContainer(id string, options *AttachOptions) (io.ReadCloser, error) StopContainer(id string, timeout int) error RestartContainer(id string, timeout int) error KillContainer(id, signal string) error @@ -41,6 +48,7 @@ type Client interface { RemoveContainer(id string, force, volumes bool) error ListImages(all bool) ([]*Image, error) RemoveImage(name string, force bool) ([]*ImageDelete, error) + SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error) PauseContainer(name string) error UnpauseContainer(name string) error RenameContainer(oldName string, newName string) error @@ -53,6 +61,6 @@ type Client interface { InspectNetwork(id string) (*NetworkResource, error) CreateNetwork(config *NetworkCreate) (*NetworkCreateResponse, error) ConnectNetwork(id, container string) error - DisconnectNetwork(id, container string) error + DisconnectNetwork(id, container string, force bool) error RemoveNetwork(id string) error } diff --git a/go/src/github.com/samalba/dockerclient/mockclient/mock.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/mockclient/mock.go old mode 100755 new mode 100644 similarity index 88% rename from go/src/github.com/samalba/dockerclient/mockclient/mock.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/mockclient/mock.go index a54ee575..9da36b87 --- a/go/src/github.com/samalba/dockerclient/mockclient/mock.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/mockclient/mock.go @@ -35,8 +35,8 @@ func (client *MockClient) InspectImage(id string) (*dockerclient.ImageInfo, erro return args.Get(0).(*dockerclient.ImageInfo), args.Error(1) } -func (client *MockClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) { - args := client.Mock.Called(config, name) +func (client *MockClient) CreateContainer(config *dockerclient.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (string, error) { + args := client.Mock.Called(config, name, authConfig) return args.String(0), args.Error(1) } @@ -50,6 +50,16 @@ func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.Container return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1) } +func (client *MockClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan dockerclient.StatsOrError, error) { + args := client.Mock.Called(id, stopChan) + return args.Get(0).(<-chan dockerclient.StatsOrError), args.Error(1) +} + +func (client *MockClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) { + args := client.Mock.Called(id, options) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + func (client *MockClient) StartContainer(id string, config *dockerclient.HostConfig) error { args := client.Mock.Called(id, config) return args.Error(0) @@ -136,6 +146,11 @@ func (client *MockClient) RemoveImage(name string, force bool) ([]*dockerclient. return args.Get(0).([]*dockerclient.ImageDelete), args.Error(1) } +func (client *MockClient) SearchImages(query, registry string, authConfig *dockerclient.AuthConfig) ([]dockerclient.ImageSearch, error) { + args := client.Mock.Called(query, registry, authConfig) + return args.Get(0).([]dockerclient.ImageSearch), args.Error(1) +} + func (client *MockClient) PauseContainer(name string) error { args := client.Mock.Called(name) return args.Error(0) @@ -211,8 +226,8 @@ func (client *MockClient) ConnectNetwork(id, container string) error { return args.Error(0) } -func (client *MockClient) DisconnectNetwork(id, container string) error { - args := client.Mock.Called(id, container) +func (client *MockClient) DisconnectNetwork(id, container string, force bool) error { + args := client.Mock.Called(id, container, force) return args.Error(0) } diff --git a/go/src/github.com/samalba/dockerclient/mockclient/mock_test.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/mockclient/mock_test.go old mode 100755 new mode 100644 similarity index 100% rename from go/src/github.com/samalba/dockerclient/mockclient/mock_test.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/mockclient/mock_test.go diff --git a/go/src/github.com/samalba/dockerclient/nopclient/nop.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/nopclient/nop.go old mode 100755 new mode 100644 similarity index 88% rename from go/src/github.com/samalba/dockerclient/nopclient/nop.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/nopclient/nop.go index 03dfbab1..659b57b7 --- a/go/src/github.com/samalba/dockerclient/nopclient/nop.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/nopclient/nop.go @@ -34,7 +34,7 @@ func (client *NopClient) InspectImage(id string) (*dockerclient.ImageInfo, error return nil, ErrNoEngine } -func (client *NopClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) { +func (client *NopClient) CreateContainer(config *dockerclient.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (string, error) { return "", ErrNoEngine } @@ -46,6 +46,14 @@ func (client *NopClient) ContainerChanges(id string) ([]*dockerclient.ContainerC return nil, ErrNoEngine } +func (client *NopClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan dockerclient.StatsOrError, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) { + return nil, ErrNoEngine +} + func (client *NopClient) StartContainer(id string, config *dockerclient.HostConfig) error { return ErrNoEngine } @@ -118,6 +126,10 @@ func (client *NopClient) RemoveImage(name string, force bool) ([]*dockerclient.I return nil, ErrNoEngine } +func (client *NopClient) SearchImages(query, registry string, authConfig *dockerclient.AuthConfig) ([]dockerclient.ImageSearch, error) { + return nil, ErrNoEngine +} + func (client *NopClient) PauseContainer(name string) error { return ErrNoEngine } @@ -178,7 +190,7 @@ func (client *NopClient) ConnectNetwork(id, container string) error { return ErrNoEngine } -func (client *NopClient) DisconnectNetwork(id, container string) error { +func (client *NopClient) DisconnectNetwork(id, container string, force bool) error { return ErrNoEngine } diff --git a/go/src/goproxy/vendor/github.com/samalba/dockerclient/tls.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/tls.go new file mode 100644 index 00000000..80ae6f1e --- /dev/null +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/tls.go @@ -0,0 +1,38 @@ +package dockerclient + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "io/ioutil" + "path/filepath" +) + +// TLSConfigFromCertPath returns a configuration based on PEM files in the directory +// +// path is usually what is set by the environment variable `DOCKER_CERT_PATH`, +// or `$HOME/.docker`. +func TLSConfigFromCertPath(path string) (*tls.Config, error) { + cert, err := ioutil.ReadFile(filepath.Join(path, "cert.pem")) + if err != nil { + return nil, err + } + key, err := ioutil.ReadFile(filepath.Join(path, "key.pem")) + if err != nil { + return nil, err + } + ca, err := ioutil.ReadFile(filepath.Join(path, "ca.pem")) + if err != nil { + return nil, err + } + tlsCert, err := tls.X509KeyPair(cert, key) + if err != nil { + return nil, err + } + tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}} + tlsConfig.RootCAs = x509.NewCertPool() + if !tlsConfig.RootCAs.AppendCertsFromPEM(ca) { + return nil, errors.New("Could not add RootCA pem") + } + return tlsConfig, nil +} diff --git a/go/src/github.com/samalba/dockerclient/types.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/types.go old mode 100755 new mode 100644 similarity index 72% rename from go/src/github.com/samalba/dockerclient/types.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/types.go index 7b64c89d..7ba79915 --- a/go/src/github.com/samalba/dockerclient/types.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/types.go @@ -5,7 +5,7 @@ import ( "io" "time" - "github.com/docker/docker/pkg/units" + "github.com/docker/go-units" ) type ContainerConfig struct { @@ -43,49 +43,70 @@ type ContainerConfig struct { // This is used only by the create command HostConfig HostConfig + + // Network configuration support + NetworkingConfig NetworkingConfig } type HostConfig struct { - Binds []string - ContainerIDFile string - LxcConf []map[string]string - Memory int64 - MemoryReservation int64 - MemorySwap int64 - KernelMemory int64 - CpuShares int64 - CpuPeriod int64 - CpusetCpus string - CpusetMems string - CpuQuota int64 - BlkioWeight int64 - OomKillDisable bool - MemorySwappiness int64 - Privileged bool - PortBindings map[string][]PortBinding - Links []string - PublishAllPorts bool - Dns []string - DNSOptions []string - DnsSearch []string - ExtraHosts []string - VolumesFrom []string - Devices []DeviceMapping - NetworkMode string - IpcMode string - PidMode string - UTSMode string - CapAdd []string - CapDrop []string - GroupAdd []string - RestartPolicy RestartPolicy - SecurityOpt []string - ReadonlyRootfs bool - Ulimits []Ulimit - LogConfig LogConfig - CgroupParent string - ConsoleSize [2]int - VolumeDriver string + Binds []string + ContainerIDFile string + LxcConf []map[string]string + Memory int64 + MemoryReservation int64 + MemorySwap int64 + KernelMemory int64 + CpuShares int64 + CpuPeriod int64 + CpusetCpus string + CpusetMems string + CpuQuota int64 + BlkioWeight int64 + OomKillDisable bool + MemorySwappiness int64 + Privileged bool + PortBindings map[string][]PortBinding + Links []string + PublishAllPorts bool + Dns []string + DNSOptions []string + DnsSearch []string + ExtraHosts []string + VolumesFrom []string + Devices []DeviceMapping + NetworkMode string + IpcMode string + PidMode string + UTSMode string + CapAdd []string + CapDrop []string + GroupAdd []string + RestartPolicy RestartPolicy + SecurityOpt []string + ReadonlyRootfs bool + Ulimits []Ulimit + LogConfig LogConfig + CgroupParent string + ConsoleSize [2]int + VolumeDriver string + OomScoreAdj int + Tmpfs map[string]string + ShmSize int64 + BlkioWeightDevice []WeightDevice + BlkioDeviceReadBps []ThrottleDevice + BlkioDeviceWriteBps []ThrottleDevice + BlkioDeviceReadIOps []ThrottleDevice + BlkioDeviceWriteIOps []ThrottleDevice +} + +type WeightDevice struct { + Path string + Weight uint16 +} + +type ThrottleDevice struct { + Path string + Rate uint64 } type DeviceMapping struct { @@ -112,6 +133,14 @@ type LogOptions struct { Tail int64 } +type AttachOptions struct { + Logs bool + Stream bool + Stdin bool + Stdout bool + Stderr bool +} + type MonitorEventsFilters struct { Event string `json:",omitempty"` Image string `json:",omitempty"` @@ -209,6 +238,14 @@ type ImageInfo struct { VirtualSize int64 } +type ImageSearch struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty"` + IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty"` +} + type ContainerInfo struct { Id string Created string @@ -225,6 +262,7 @@ type ContainerInfo struct { Gateway string Bridge string Ports map[string][]PortBinding + Networks map[string]*EndpointSettings } SysInitPath string ResolvConfPath string @@ -244,24 +282,62 @@ type Port struct { Type string } +// EndpointSettings stores the network endpoint details +type EndpointSettings struct { + // Configurations + IPAMConfig *EndpointIPAMConfig + Links []string + Aliases []string + // Operational data + NetworkID string + EndpointID string + Gateway string + IPAddress string + IPPrefixLen int + IPv6Gateway string + GlobalIPv6Address string + GlobalIPv6PrefixLen int + MacAddress string +} + +// NetworkingConfig represents the container's networking configuration for each of its interfaces +// Carries the networink configs specified in the `docker run` and `docker network connect` commands +type NetworkingConfig struct { + EndpointsConfig map[string]*EndpointSettings // Endpoint configs for each conencting network +} + type Container struct { - Id string - Names []string - Image string - Command string - Created int64 - Status string - Ports []Port - SizeRw int64 - SizeRootFs int64 - Labels map[string]string + Id string + Names []string + Image string + Command string + Created int64 + Status string + Ports []Port + SizeRw int64 + SizeRootFs int64 + Labels map[string]string + NetworkSettings struct { + Networks map[string]EndpointSettings + } +} + +type Actor struct { + ID string + Attributes map[string]string } type Event struct { - Id string - Status string - From string - Time int64 + Status string `json:"status,omitempty"` + ID string `json:"id,omitempty"` + From string `json:"from,omitempty"` + + Type string + Action string + Actor Actor + + Time int64 `json:"time,omitempty"` + TimeNano int64 `json:"timeNano,omitempty"` } type Version struct { @@ -331,6 +407,11 @@ type ImageDelete struct { Untagged string } +type StatsOrError struct { + Stats + Error error +} + type EventOrError struct { Event Error error @@ -356,6 +437,7 @@ type ThrottlingData struct { ThrottledTime uint64 `json:"throttled_time"` } +// All CPU stats are aggregated since container inception. type CpuUsage struct { // Total CPU time consumed. // Units: nanoseconds. @@ -474,8 +556,9 @@ type VolumeCreateRequest struct { // IPAM represents IP Address Management type IPAM struct { - Driver string - Config []IPAMConfig + Driver string + Options map[string]string //Per network IPAM driver options + Config []IPAMConfig } // IPAMConfig represents IPAM configurations @@ -486,18 +569,27 @@ type IPAMConfig struct { AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"` } +// EndpointIPAMConfig represents IPAM configurations for the endpoint +type EndpointIPAMConfig struct { + IPv4Address string `json:",omitempty"` + IPv6Address string `json:",omitempty"` +} + // NetworkResource is the body of the "get network" http response message type NetworkResource struct { - Name string - ID string `json:"Id"` - Scope string - Driver string - IPAM IPAM + Name string + ID string `json:"Id"` + Scope string + Driver string + IPAM IPAM + //Internal bool Containers map[string]EndpointResource + Options map[string]string } -//EndpointResource contains network resources allocated and usd for a container in a network +// EndpointResource contains network resources allocated and used for a container in a network type EndpointResource struct { + Name string EndpointID string MacAddress string IPv4Address string @@ -510,6 +602,8 @@ type NetworkCreate struct { CheckDuplicate bool Driver string IPAM IPAM + Internal bool + Options map[string]string } // NetworkCreateResponse is the response message sent by the server for network create call @@ -526,4 +620,5 @@ type NetworkConnect struct { // NetworkDisconnect represents the data to be used to disconnect a container from the network type NetworkDisconnect struct { Container string + Force bool } diff --git a/go/src/github.com/samalba/dockerclient/utils.go b/go/src/goproxy/vendor/github.com/samalba/dockerclient/utils.go old mode 100755 new mode 100644 similarity index 62% rename from go/src/github.com/samalba/dockerclient/utils.go rename to go/src/goproxy/vendor/github.com/samalba/dockerclient/utils.go index 806f1b3e..8a6b0d6e --- a/go/src/github.com/samalba/dockerclient/utils.go +++ b/go/src/goproxy/vendor/github.com/samalba/dockerclient/utils.go @@ -8,7 +8,9 @@ import ( "time" ) -func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client { +type tcpFunc func(*net.TCPConn, time.Duration) error + +func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration, setUserTimeout tcpFunc) *http.Client { httpTransport := &http.Transport{ TLSClientConfig: tlsConfig, } @@ -16,7 +18,13 @@ func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *ht switch u.Scheme { default: httpTransport.Dial = func(proto, addr string) (net.Conn, error) { - return net.DialTimeout(proto, addr, timeout) + conn, err := net.DialTimeout(proto, addr, timeout) + if tcpConn, ok := conn.(*net.TCPConn); ok && setUserTimeout != nil { + // Sender can break TCP connection if the remote side doesn't + // acknowledge packets within timeout + setUserTimeout(tcpConn, timeout) + } + return conn, err } case "unix": socketPath := u.Path diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/.travis.yml b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/.travis.yml new file mode 100644 index 00000000..ff23150d --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - 1.3 + - 1.4 + - 1.5 + - tip +install: + - go get -t ./... +script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/CHANGELOG.md new file mode 100644 index 00000000..9e9e6009 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/CHANGELOG.md @@ -0,0 +1,59 @@ +# 0.9.0 + +* logrus/text_formatter: don't emit empty msg +* logrus/hooks/airbrake: move out of main repository +* logrus/hooks/sentry: move out of main repository +* logrus/hooks/papertrail: move out of main repository +* logrus/hooks/bugsnag: move out of main repository +* logrus/core: run tests with `-race` +* logrus/core: detect TTY based on `stderr` +* logrus/core: support `WithError` on logger +* logrus/core: Solaris support + +# 0.8.7 + +* logrus/core: fix possible race (#216) +* logrus/doc: small typo fixes and doc improvements + + +# 0.8.6 + +* hooks/raven: allow passing an initialized client + +# 0.8.5 + +* logrus/core: revert #208 + +# 0.8.4 + +* formatter/text: fix data race (#218) + +# 0.8.3 + +* logrus/core: fix entry log level (#208) +* logrus/core: improve performance of text formatter by 40% +* logrus/core: expose `LevelHooks` type +* logrus/core: add support for DragonflyBSD and NetBSD +* formatter/text: print structs more verbosely + +# 0.8.2 + +* logrus: fix more Fatal family functions + +# 0.8.1 + +* logrus: fix not exiting on `Fatalf` and `Fatalln` + +# 0.8.0 + +* logrus: defaults to stderr instead of stdout +* hooks/sentry: add special field for `*http.Request` +* formatter/text: ignore Windows for colors + +# 0.7.3 + +* formatter/\*: allow configuration of timestamp layout + +# 0.7.2 + +* formatter/text: Add configuration option for time format (#158) diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/LICENSE b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/LICENSE new file mode 100644 index 00000000..f090cb42 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/README.md b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/README.md new file mode 100644 index 00000000..d3a1d4cf --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/README.md @@ -0,0 +1,385 @@ +# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/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). Logrus itself is completely stable and has been used in +many large deployments. The core API is unlikely to change much but please +version control your Logrus to make sure you aren't fetching latest `master` on +every build.** + +Nicely color-coded in development (when a TTY is attached, otherwise just +plain text): + +![Colored](http://i.imgur.com/PY7qMwd.png) + +With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash +or Splunk: + +```json +{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the +ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} + +{"level":"warning","msg":"The group's number increased tremendously!", +"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"A giant walrus appears!", +"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", +"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} + +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, +"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +``` + +With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not +attached, the output is compatible with the +[logfmt](http://godoc.org/github.com/kr/logfmt) format: + +```text +time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 +time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 +time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true +time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 +time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +exit status 1 +``` + +#### Example + +The simplest way to use Logrus is simply the package-level exported logger: + +```go +package main + +import ( + log "github.com/Sirupsen/logrus" +) + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + }).Info("A walrus appears") +} +``` + +Note that it's completely api-compatible with the stdlib logger, so you can +replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` +and you'll now have the flexibility of Logrus. You can customize it all you +want: + +```go +package main + +import ( + "os" + log "github.com/Sirupsen/logrus" +) + +func init() { + // Log as JSON instead of the default ASCII formatter. + log.SetFormatter(&log.JSONFormatter{}) + + // Output to stderr instead of stdout, could also be a file. + log.SetOutput(os.Stderr) + + // Only log the warning severity or above. + log.SetLevel(log.WarnLevel) +} + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(log.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(log.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") + + // A common pattern is to re-use fields between logging statements by re-using + // the logrus.Entry returned from WithFields() + contextLogger := log.WithFields(log.Fields{ + "common": "this is a common field", + "other": "I also should be logged always", + }) + + contextLogger.Info("I'll be logged with common and other field") + contextLogger.Info("Me too") +} +``` + +For more advanced usage such as logging to multiple locations from the same +application, you can also create an instance of the `logrus` Logger: + +```go +package main + +import ( + "github.com/Sirupsen/logrus" +) + +// Create a new instance of the logger. You can have any number of instances. +var log = logrus.New() + +func main() { + // The API for setting attributes is a little different than the package level + // exported logger. See Godoc. + log.Out = os.Stderr + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") +} +``` + +#### Fields + +Logrus encourages careful, structured logging though logging fields instead of +long, unparseable error messages. For example, instead of: `log.Fatalf("Failed +to send event %s to topic %s with key %d")`, you should log the much more +discoverable: + +```go +log.WithFields(log.Fields{ + "event": event, + "topic": topic, + "key": key, +}).Fatal("Failed to send event") +``` + +We've found this API forces you to think about logging in a way that produces +much more useful logging messages. We've been in countless situations where just +a single added field to a log statement that was already there would've saved us +hours. The `WithFields` call is optional. + +In general, with Logrus using any of the `printf`-family functions should be +seen as a hint you should add a field, however, you can still use the +`printf`-family functions with Logrus. + +#### Hooks + +You can add hooks for logging levels. For example to send errors to an exception +tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to +multiple places simultaneously, e.g. syslog. + +Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in +`init`: + +```go +import ( + log "github.com/Sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "log/syslog" +) + +func init() { + + // Use the Airbrake hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + log.AddHook(airbrake.NewHook(123, "xyz", "production")) + + hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + if err != nil { + log.Error("Unable to connect to local syslog daemon") + } else { + log.AddHook(hook) + } +} +``` +Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). + +| Hook | Description | +| ----- | ----------- | +| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | +| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | +| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. | +| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. | +| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. | +| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | +| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | +| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | +| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) | +| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | +| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | +| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | +| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | +| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | +| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | +| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | +| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | +| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit | +| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | +| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | +| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | +| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | + +#### Level logging + +Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic. + +```go +log.Debug("Useful debugging information.") +log.Info("Something noteworthy happened!") +log.Warn("You should probably take a look at this.") +log.Error("Something failed but I'm not quitting.") +// Calls os.Exit(1) after logging +log.Fatal("Bye.") +// Calls panic() after logging +log.Panic("I'm bailing.") +``` + +You can set the logging level on a `Logger`, then it will only log entries with +that severity or anything above it: + +```go +// Will log anything that is info or above (warn, error, fatal, panic). Default. +log.SetLevel(log.InfoLevel) +``` + +It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose +environment if your application has that. + +#### Entries + +Besides the fields added with `WithField` or `WithFields` some fields are +automatically added to all logging events: + +1. `time`. The timestamp when the entry was created. +2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after + the `AddFields` call. E.g. `Failed to send event.` +3. `level`. The logging level. E.g. `info`. + +#### Environments + +Logrus has no notion of environment. + +If you wish for hooks and formatters to only be used in specific environments, +you should handle that yourself. For example, if your application has a global +variable `Environment`, which is a string representation of the environment you +could do: + +```go +import ( + log "github.com/Sirupsen/logrus" +) + +init() { + // do something here to set environment depending on an environment variable + // or command-line flag + if Environment == "production" { + log.SetFormatter(&log.JSONFormatter{}) + } else { + // The TextFormatter is default, you don't actually have to do this. + log.SetFormatter(&log.TextFormatter{}) + } +} +``` + +This configuration is how `logrus` was intended to be used, but JSON in +production is mostly only useful if you do log aggregation with tools like +Splunk or Logstash. + +#### Formatters + +The built-in logging formatters are: + +* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise + without colors. + * *Note:* to force colored output when there is no TTY, set the `ForceColors` + field to `true`. To force no colored output even if there is a TTY set the + `DisableColors` field to `true` +* `logrus.JSONFormatter`. Logs fields as JSON. +* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events. + + ```go + logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"}) + ``` + +Third party logging formatters: + +* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. +* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. + +You can define your formatter by implementing the `Formatter` interface, +requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a +`Fields` type (`map[string]interface{}`) with all your fields as well as the +default ones (see Entries section above): + +```go +type MyJSONFormatter struct { +} + +log.SetFormatter(new(MyJSONFormatter)) + +func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { + // Note this doesn't include Time, Level and Message which are available on + // the Entry. Consult `godoc` on information about those fields or read the + // source of the official loggers. + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} +``` + +#### Logger as an `io.Writer` + +Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. + +```go +w := logger.Writer() +defer w.Close() + +srv := http.Server{ + // create a stdlib log.Logger that writes to + // logrus.Logger. + ErrorLog: log.New(w, "", 0), +} +``` + +Each line written to that writer will be printed the usual way, using formatters +and hooks. The level for those entries is `info`. + +#### Rotation + +Log rotation is not provided with Logrus. Log rotation should be done by an +external program (like `logrotate(8)`) that can compress and delete old log +entries. It should not be a feature of the application-level logger. + +#### Tools + +| Tool | Description | +| ---- | ----------- | +|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| + +#### Testing + +Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: + +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook +* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): + +```go +logger, hook := NewNullLogger() +logger.Error("Hello error") + +assert.Equal(1, len(hook.Entries)) +assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) +assert.Equal("Hello error", hook.LastEntry().Message) + +hook.Reset() +assert.Nil(hook.LastEntry()) +``` diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/doc.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/doc.go new file mode 100644 index 00000000..dddd5f87 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/doc.go @@ -0,0 +1,26 @@ +/* +Package logrus is a structured logger for Go, completely API compatible with the standard library logger. + + +The simplest way to use Logrus is simply the package-level exported logger: + + package main + + import ( + log "github.com/Sirupsen/logrus" + ) + + func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "number": 1, + "size": 10, + }).Info("A walrus appears") + } + +Output: + time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 + +For a full guide visit https://github.com/Sirupsen/logrus +*/ +package logrus diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/entry.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/entry.go new file mode 100644 index 00000000..9ae900bc --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/entry.go @@ -0,0 +1,264 @@ +package logrus + +import ( + "bytes" + "fmt" + "io" + "os" + "time" +) + +// Defines the key when adding errors using WithError. +var ErrorKey = "error" + +// An entry is the final or intermediate Logrus logging entry. It contains all +// the fields passed with WithField{,s}. It's finally logged when Debug, Info, +// Warn, Error, Fatal or Panic is called on it. These objects can be reused and +// passed around as much as you wish to avoid field duplication. +type Entry struct { + Logger *Logger + + // Contains all the fields set by the user. + Data Fields + + // Time at which the log entry was created + Time time.Time + + // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + Level Level + + // Message passed to Debug, Info, Warn, Error, Fatal or Panic + Message string +} + +func NewEntry(logger *Logger) *Entry { + return &Entry{ + Logger: logger, + // Default is three fields, give a little extra room + Data: make(Fields, 5), + } +} + +// Returns a reader for the entry, which is a proxy to the formatter. +func (entry *Entry) Reader() (*bytes.Buffer, error) { + serialized, err := entry.Logger.Formatter.Format(entry) + return bytes.NewBuffer(serialized), err +} + +// Returns the string representation from the reader and ultimately the +// formatter. +func (entry *Entry) String() (string, error) { + reader, err := entry.Reader() + if err != nil { + return "", err + } + + return reader.String(), err +} + +// Add an error as single field (using the key defined in ErrorKey) to the Entry. +func (entry *Entry) WithError(err error) *Entry { + return entry.WithField(ErrorKey, err) +} + +// Add a single field to the Entry. +func (entry *Entry) WithField(key string, value interface{}) *Entry { + return entry.WithFields(Fields{key: value}) +} + +// Add a map of fields to the Entry. +func (entry *Entry) WithFields(fields Fields) *Entry { + data := Fields{} + for k, v := range entry.Data { + data[k] = v + } + for k, v := range fields { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data} +} + +// This function is not declared with a pointer value because otherwise +// race conditions will occur when using multiple goroutines +func (entry Entry) log(level Level, msg string) { + entry.Time = time.Now() + entry.Level = level + entry.Message = msg + + if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + entry.Logger.mu.Unlock() + } + + reader, err := entry.Reader() + if err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + entry.Logger.mu.Unlock() + } + + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + + _, err = io.Copy(entry.Logger.Out, reader) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + + // To avoid Entry#log() returning a value that only would make sense for + // panic() to use in Entry#Panic(), we avoid the allocation by checking + // directly here. + if level <= PanicLevel { + panic(&entry) + } +} + +func (entry *Entry) Debug(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.log(DebugLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Print(args ...interface{}) { + entry.Info(args...) +} + +func (entry *Entry) Info(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.log(InfoLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warn(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.log(WarnLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warning(args ...interface{}) { + entry.Warn(args...) +} + +func (entry *Entry) Error(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.log(ErrorLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Fatal(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.log(FatalLevel, fmt.Sprint(args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panic(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.log(PanicLevel, fmt.Sprint(args...)) + } + panic(fmt.Sprint(args...)) +} + +// Entry Printf family functions + +func (entry *Entry) Debugf(format string, args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Infof(format string, args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Infof(format, args...) +} + +func (entry *Entry) Warnf(format string, args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warnf(format, args...) +} + +func (entry *Entry) Errorf(format string, args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Fatalf(format string, args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(fmt.Sprintf(format, args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panicf(format string, args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(fmt.Sprintf(format, args...)) + } +} + +// Entry Println family functions + +func (entry *Entry) Debugln(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Infoln(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Println(args ...interface{}) { + entry.Infoln(args...) +} + +func (entry *Entry) Warnln(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warnln(args...) +} + +func (entry *Entry) Errorln(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Fatalln(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(entry.sprintlnn(args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panicln(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(entry.sprintlnn(args...)) + } +} + +// Sprintlnn => Sprint no newline. This is to get the behavior of how +// fmt.Sprintln where spaces are always added between operands, regardless of +// their type. Instead of vendoring the Sprintln implementation to spare a +// string allocation, we do the simplest thing. +func (entry *Entry) sprintlnn(args ...interface{}) string { + msg := fmt.Sprintln(args...) + return msg[:len(msg)-1] +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/entry_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/entry_test.go new file mode 100644 index 00000000..99c3b41d --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/entry_test.go @@ -0,0 +1,77 @@ +package logrus + +import ( + "bytes" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEntryWithError(t *testing.T) { + + assert := assert.New(t) + + defer func() { + ErrorKey = "error" + }() + + err := fmt.Errorf("kaboom at layer %d", 4711) + + assert.Equal(err, WithError(err).Data["error"]) + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + + assert.Equal(err, entry.WithError(err).Data["error"]) + + ErrorKey = "err" + + assert.Equal(err, entry.WithError(err).Data["err"]) + +} + +func TestEntryPanicln(t *testing.T) { + errBoom := fmt.Errorf("boom time") + + defer func() { + p := recover() + assert.NotNil(t, p) + + switch pVal := p.(type) { + case *Entry: + assert.Equal(t, "kaboom", pVal.Message) + assert.Equal(t, errBoom, pVal.Data["err"]) + default: + t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) + } + }() + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + entry.WithField("err", errBoom).Panicln("kaboom") +} + +func TestEntryPanicf(t *testing.T) { + errBoom := fmt.Errorf("boom again") + + defer func() { + p := recover() + assert.NotNil(t, p) + + switch pVal := p.(type) { + case *Entry: + assert.Equal(t, "kaboom true", pVal.Message) + assert.Equal(t, errBoom, pVal.Data["err"]) + default: + t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) + } + }() + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + entry.WithField("err", errBoom).Panicf("kaboom %v", true) +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go new file mode 100644 index 00000000..a1623ec0 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go @@ -0,0 +1,50 @@ +package main + +import ( + "github.com/Sirupsen/logrus" +) + +var log = logrus.New() + +func init() { + log.Formatter = new(logrus.JSONFormatter) + log.Formatter = new(logrus.TextFormatter) // default + log.Level = logrus.DebugLevel +} + +func main() { + defer func() { + err := recover() + if err != nil { + log.WithFields(logrus.Fields{ + "omg": true, + "err": err, + "number": 100, + }).Fatal("The ice breaks!") + } + }() + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "number": 8, + }).Debug("Started observing beach") + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "temperature": -4, + }).Debug("Temperature changes") + + log.WithFields(logrus.Fields{ + "animal": "orca", + "size": 9009, + }).Panic("It's over 9000!") +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go new file mode 100644 index 00000000..3187f6d3 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/Sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" +) + +var log = logrus.New() + +func init() { + log.Formatter = new(logrus.TextFormatter) // default + log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) +} + +func main() { + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/exported.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/exported.go new file mode 100644 index 00000000..9a0120ac --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/exported.go @@ -0,0 +1,193 @@ +package logrus + +import ( + "io" +) + +var ( + // std is the name of the standard logger in stdlib `log` + std = New() +) + +func StandardLogger() *Logger { + return std +} + +// SetOutput sets the standard logger output. +func SetOutput(out io.Writer) { + std.mu.Lock() + defer std.mu.Unlock() + std.Out = out +} + +// SetFormatter sets the standard logger formatter. +func SetFormatter(formatter Formatter) { + std.mu.Lock() + defer std.mu.Unlock() + std.Formatter = formatter +} + +// SetLevel sets the standard logger level. +func SetLevel(level Level) { + std.mu.Lock() + defer std.mu.Unlock() + std.Level = level +} + +// GetLevel returns the standard logger level. +func GetLevel() Level { + std.mu.Lock() + defer std.mu.Unlock() + return std.Level +} + +// AddHook adds a hook to the standard logger hooks. +func AddHook(hook Hook) { + std.mu.Lock() + defer std.mu.Unlock() + std.Hooks.Add(hook) +} + +// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +func WithError(err error) *Entry { + return std.WithField(ErrorKey, err) +} + +// WithField creates an entry from the standard logger and adds a field to +// it. If you want multiple fields, use `WithFields`. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithField(key string, value interface{}) *Entry { + return std.WithField(key, value) +} + +// WithFields creates an entry from the standard logger and adds multiple +// fields to it. This is simply a helper for `WithField`, invoking it +// once for each field. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithFields(fields Fields) *Entry { + return std.WithFields(fields) +} + +// Debug logs a message at level Debug on the standard logger. +func Debug(args ...interface{}) { + std.Debug(args...) +} + +// Print logs a message at level Info on the standard logger. +func Print(args ...interface{}) { + std.Print(args...) +} + +// Info logs a message at level Info on the standard logger. +func Info(args ...interface{}) { + std.Info(args...) +} + +// Warn logs a message at level Warn on the standard logger. +func Warn(args ...interface{}) { + std.Warn(args...) +} + +// Warning logs a message at level Warn on the standard logger. +func Warning(args ...interface{}) { + std.Warning(args...) +} + +// Error logs a message at level Error on the standard logger. +func Error(args ...interface{}) { + std.Error(args...) +} + +// Panic logs a message at level Panic on the standard logger. +func Panic(args ...interface{}) { + std.Panic(args...) +} + +// Fatal logs a message at level Fatal on the standard logger. +func Fatal(args ...interface{}) { + std.Fatal(args...) +} + +// Debugf logs a message at level Debug on the standard logger. +func Debugf(format string, args ...interface{}) { + std.Debugf(format, args...) +} + +// Printf logs a message at level Info on the standard logger. +func Printf(format string, args ...interface{}) { + std.Printf(format, args...) +} + +// Infof logs a message at level Info on the standard logger. +func Infof(format string, args ...interface{}) { + std.Infof(format, args...) +} + +// Warnf logs a message at level Warn on the standard logger. +func Warnf(format string, args ...interface{}) { + std.Warnf(format, args...) +} + +// Warningf logs a message at level Warn on the standard logger. +func Warningf(format string, args ...interface{}) { + std.Warningf(format, args...) +} + +// Errorf logs a message at level Error on the standard logger. +func Errorf(format string, args ...interface{}) { + std.Errorf(format, args...) +} + +// Panicf logs a message at level Panic on the standard logger. +func Panicf(format string, args ...interface{}) { + std.Panicf(format, args...) +} + +// Fatalf logs a message at level Fatal on the standard logger. +func Fatalf(format string, args ...interface{}) { + std.Fatalf(format, args...) +} + +// Debugln logs a message at level Debug on the standard logger. +func Debugln(args ...interface{}) { + std.Debugln(args...) +} + +// Println logs a message at level Info on the standard logger. +func Println(args ...interface{}) { + std.Println(args...) +} + +// Infoln logs a message at level Info on the standard logger. +func Infoln(args ...interface{}) { + std.Infoln(args...) +} + +// Warnln logs a message at level Warn on the standard logger. +func Warnln(args ...interface{}) { + std.Warnln(args...) +} + +// Warningln logs a message at level Warn on the standard logger. +func Warningln(args ...interface{}) { + std.Warningln(args...) +} + +// Errorln logs a message at level Error on the standard logger. +func Errorln(args ...interface{}) { + std.Errorln(args...) +} + +// Panicln logs a message at level Panic on the standard logger. +func Panicln(args ...interface{}) { + std.Panicln(args...) +} + +// Fatalln logs a message at level Fatal on the standard logger. +func Fatalln(args ...interface{}) { + std.Fatalln(args...) +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatter.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatter.go new file mode 100644 index 00000000..104d689f --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatter.go @@ -0,0 +1,48 @@ +package logrus + +import "time" + +const DefaultTimestampFormat = time.RFC3339 + +// The Formatter interface is used to implement a custom Formatter. It takes an +// `Entry`. It exposes all the fields, including the default ones: +// +// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. +// * `entry.Data["time"]`. The timestamp. +// * `entry.Data["level"]. The level the entry was logged at. +// +// Any additional fields added with `WithField` or `WithFields` are also in +// `entry.Data`. Format is expected to return an array of bytes which are then +// logged to `logger.Out`. +type Formatter interface { + Format(*Entry) ([]byte, error) +} + +// This is to not silently overwrite `time`, `msg` and `level` fields when +// dumping it. If this code wasn't there doing: +// +// logrus.WithField("level", 1).Info("hello") +// +// Would just silently drop the user provided level. Instead with this code +// it'll logged as: +// +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// +// It's not exported because it's still using Data in an opinionated way. It's to +// avoid code duplication between the two default formatters. +func prefixFieldClashes(data Fields) { + _, ok := data["time"] + if ok { + data["fields.time"] = data["time"] + } + + _, ok = data["msg"] + if ok { + data["fields.msg"] = data["msg"] + } + + _, ok = data["level"] + if ok { + data["fields.level"] = data["level"] + } +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go new file mode 100644 index 00000000..c6d290c7 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go @@ -0,0 +1,98 @@ +package logrus + +import ( + "fmt" + "testing" + "time" +) + +// smallFields is a small size data set for benchmarking +var smallFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", +} + +// largeFields is a large size data set for benchmarking +var largeFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", + "five": "six", + "seven": "eight", + "nine": "ten", + "eleven": "twelve", + "thirteen": "fourteen", + "fifteen": "sixteen", + "seventeen": "eighteen", + "nineteen": "twenty", + "a": "b", + "c": "d", + "e": "f", + "g": "h", + "i": "j", + "k": "l", + "m": "n", + "o": "p", + "q": "r", + "s": "t", + "u": "v", + "w": "x", + "y": "z", + "this": "will", + "make": "thirty", + "entries": "yeah", +} + +var errorFields = Fields{ + "foo": fmt.Errorf("bar"), + "baz": fmt.Errorf("qux"), +} + +func BenchmarkErrorTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields) +} + +func BenchmarkSmallTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) +} + +func BenchmarkLargeTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) +} + +func BenchmarkSmallColoredTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) +} + +func BenchmarkLargeColoredTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) +} + +func BenchmarkSmallJSONFormatter(b *testing.B) { + doBenchmark(b, &JSONFormatter{}, smallFields) +} + +func BenchmarkLargeJSONFormatter(b *testing.B) { + doBenchmark(b, &JSONFormatter{}, largeFields) +} + +func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { + entry := &Entry{ + Time: time.Time{}, + Level: InfoLevel, + Message: "message", + Data: fields, + } + var d []byte + var err error + for i := 0; i < b.N; i++ { + d, err = formatter.Format(entry) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(len(d))) + } +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go new file mode 100644 index 00000000..aad646ab --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go @@ -0,0 +1,61 @@ +package logstash + +import ( + "encoding/json" + "fmt" + + "github.com/Sirupsen/logrus" +) + +// Formatter generates json in logstash format. +// Logstash site: http://logstash.net/ +type LogstashFormatter struct { + Type string // if not empty use for logstash type field. + + // TimestampFormat sets the format used for timestamps. + TimestampFormat string +} + +func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { + fields := make(logrus.Fields) + for k, v := range entry.Data { + fields[k] = v + } + + fields["@version"] = 1 + + if f.TimestampFormat == "" { + f.TimestampFormat = logrus.DefaultTimestampFormat + } + + fields["@timestamp"] = entry.Time.Format(f.TimestampFormat) + + // set message field + v, ok := entry.Data["message"] + if ok { + fields["fields.message"] = v + } + fields["message"] = entry.Message + + // set level field + v, ok = entry.Data["level"] + if ok { + fields["fields.level"] = v + } + fields["level"] = entry.Level.String() + + // set type field + if f.Type != "" { + v, ok = entry.Data["type"] + if ok { + fields["fields.type"] = v + } + fields["type"] = f.Type + } + + serialized, err := json.Marshal(fields) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go new file mode 100644 index 00000000..d8814a0e --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go @@ -0,0 +1,52 @@ +package logstash + +import ( + "bytes" + "encoding/json" + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestLogstashFormatter(t *testing.T) { + assert := assert.New(t) + + lf := LogstashFormatter{Type: "abc"} + + fields := logrus.Fields{ + "message": "def", + "level": "ijk", + "type": "lmn", + "one": 1, + "pi": 3.14, + "bool": true, + } + + entry := logrus.WithFields(fields) + entry.Message = "msg" + entry.Level = logrus.InfoLevel + + b, _ := lf.Format(entry) + + var data map[string]interface{} + dec := json.NewDecoder(bytes.NewReader(b)) + dec.UseNumber() + dec.Decode(&data) + + // base fields + assert.Equal(json.Number("1"), data["@version"]) + assert.NotEmpty(data["@timestamp"]) + assert.Equal("abc", data["type"]) + assert.Equal("msg", data["message"]) + assert.Equal("info", data["level"]) + + // substituted fields + assert.Equal("def", data["fields.message"]) + assert.Equal("ijk", data["fields.level"]) + assert.Equal("lmn", data["fields.type"]) + + // formats + assert.Equal(json.Number("1"), data["one"]) + assert.Equal(json.Number("3.14"), data["pi"]) + assert.Equal(true, data["bool"]) +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hook_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hook_test.go new file mode 100644 index 00000000..13f34cb6 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hook_test.go @@ -0,0 +1,122 @@ +package logrus + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type TestHook struct { + Fired bool +} + +func (hook *TestHook) Fire(entry *Entry) error { + hook.Fired = true + return nil +} + +func (hook *TestHook) Levels() []Level { + return []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + PanicLevel, + } +} + +func TestHookFires(t *testing.T) { + hook := new(TestHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + assert.Equal(t, hook.Fired, false) + + log.Print("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} + +type ModifyHook struct { +} + +func (hook *ModifyHook) Fire(entry *Entry) error { + entry.Data["wow"] = "whale" + return nil +} + +func (hook *ModifyHook) Levels() []Level { + return []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + PanicLevel, + } +} + +func TestHookCanModifyEntry(t *testing.T) { + hook := new(ModifyHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.WithField("wow", "elephant").Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["wow"], "whale") + }) +} + +func TestCanFireMultipleHooks(t *testing.T) { + hook1 := new(ModifyHook) + hook2 := new(TestHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook1) + log.Hooks.Add(hook2) + + log.WithField("wow", "elephant").Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["wow"], "whale") + assert.Equal(t, hook2.Fired, true) + }) +} + +type ErrorHook struct { + Fired bool +} + +func (hook *ErrorHook) Fire(entry *Entry) error { + hook.Fired = true + return nil +} + +func (hook *ErrorHook) Levels() []Level { + return []Level{ + ErrorLevel, + } +} + +func TestErrorHookShouldntFireOnInfo(t *testing.T) { + hook := new(ErrorHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.Info("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, false) + }) +} + +func TestErrorHookShouldFireOnError(t *testing.T) { + hook := new(ErrorHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.Error("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks.go new file mode 100644 index 00000000..3f151cdc --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks.go @@ -0,0 +1,34 @@ +package logrus + +// A hook to be fired when logging on the logging levels returned from +// `Levels()` on your implementation of the interface. Note that this is not +// fired in a goroutine or a channel with workers, you should handle such +// functionality yourself if your call is non-blocking and you don't wish for +// the logging calls for levels returned from `Levels()` to block. +type Hook interface { + Levels() []Level + Fire(*Entry) error +} + +// Internal type for storing the hooks on a logger instance. +type LevelHooks map[Level][]Hook + +// Add a hook to an instance of logger. This is called with +// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. +func (hooks LevelHooks) Add(hook Hook) { + for _, level := range hook.Levels() { + hooks[level] = append(hooks[level], hook) + } +} + +// Fire all the hooks for the passed level. Used by `entry.log` to fire +// appropriate hooks for a log entry. +func (hooks LevelHooks) Fire(level Level, entry *Entry) error { + for _, hook := range hooks[level] { + if err := hook.Fire(entry); err != nil { + return err + } + } + + return nil +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md new file mode 100644 index 00000000..066704b3 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md @@ -0,0 +1,39 @@ +# Syslog Hooks for Logrus :walrus: + +## Usage + +```go +import ( + "log/syslog" + "github.com/Sirupsen/logrus" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" +) + +func main() { + log := logrus.New() + hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + + if err == nil { + log.Hooks.Add(hook) + } +} +``` + +If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following. + +```go +import ( + "log/syslog" + "github.com/Sirupsen/logrus" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" +) + +func main() { + log := logrus.New() + hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + + if err == nil { + log.Hooks.Add(hook) + } +} +``` \ No newline at end of file diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go new file mode 100644 index 00000000..a36e2003 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go @@ -0,0 +1,54 @@ +// +build !windows,!nacl,!plan9 + +package logrus_syslog + +import ( + "fmt" + "github.com/Sirupsen/logrus" + "log/syslog" + "os" +) + +// SyslogHook to send logs via syslog. +type SyslogHook struct { + Writer *syslog.Writer + SyslogNetwork string + SyslogRaddr string +} + +// Creates a hook to be added to an instance of logger. This is called with +// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")` +// `if err == nil { log.Hooks.Add(hook) }` +func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) { + w, err := syslog.Dial(network, raddr, priority, tag) + return &SyslogHook{w, network, raddr}, err +} + +func (hook *SyslogHook) Fire(entry *logrus.Entry) error { + line, err := entry.String() + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) + return err + } + + switch entry.Level { + case logrus.PanicLevel: + return hook.Writer.Crit(line) + case logrus.FatalLevel: + return hook.Writer.Crit(line) + case logrus.ErrorLevel: + return hook.Writer.Err(line) + case logrus.WarnLevel: + return hook.Writer.Warning(line) + case logrus.InfoLevel: + return hook.Writer.Info(line) + case logrus.DebugLevel: + return hook.Writer.Debug(line) + default: + return nil + } +} + +func (hook *SyslogHook) Levels() []logrus.Level { + return logrus.AllLevels +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go new file mode 100644 index 00000000..42762dc1 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go @@ -0,0 +1,26 @@ +package logrus_syslog + +import ( + "github.com/Sirupsen/logrus" + "log/syslog" + "testing" +) + +func TestLocalhostAddAndPrint(t *testing.T) { + log := logrus.New() + hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + + if err != nil { + t.Errorf("Unable to connect to local syslog.") + } + + log.Hooks.Add(hook) + + for _, level := range hook.Levels() { + if len(log.Hooks[level]) != 1 { + t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level])) + } + } + + log.Info("Congratulations!") +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/test/test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/test/test.go new file mode 100644 index 00000000..06881253 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/test/test.go @@ -0,0 +1,67 @@ +package test + +import ( + "io/ioutil" + + "github.com/Sirupsen/logrus" +) + +// test.Hook is a hook designed for dealing with logs in test scenarios. +type Hook struct { + Entries []*logrus.Entry +} + +// Installs a test hook for the global logger. +func NewGlobal() *Hook { + + hook := new(Hook) + logrus.AddHook(hook) + + return hook + +} + +// Installs a test hook for a given local logger. +func NewLocal(logger *logrus.Logger) *Hook { + + hook := new(Hook) + logger.Hooks.Add(hook) + + return hook + +} + +// Creates a discarding logger and installs the test hook. +func NewNullLogger() (*logrus.Logger, *Hook) { + + logger := logrus.New() + logger.Out = ioutil.Discard + + return logger, NewLocal(logger) + +} + +func (t *Hook) Fire(e *logrus.Entry) error { + t.Entries = append(t.Entries, e) + return nil +} + +func (t *Hook) Levels() []logrus.Level { + return logrus.AllLevels +} + +// LastEntry returns the last entry that was logged or nil. +func (t *Hook) LastEntry() (l *logrus.Entry) { + + if i := len(t.Entries) - 1; i < 0 { + return nil + } else { + return t.Entries[i] + } + +} + +// Reset removes all Entries from this test hook. +func (t *Hook) Reset() { + t.Entries = make([]*logrus.Entry, 0) +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go new file mode 100644 index 00000000..d69455ba --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go @@ -0,0 +1,39 @@ +package test + +import ( + "testing" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestAllHooks(t *testing.T) { + + assert := assert.New(t) + + logger, hook := NewNullLogger() + assert.Nil(hook.LastEntry()) + assert.Equal(0, len(hook.Entries)) + + logger.Error("Hello error") + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) + + logger.Warn("Hello warning") + assert.Equal(logrus.WarnLevel, hook.LastEntry().Level) + assert.Equal("Hello warning", hook.LastEntry().Message) + assert.Equal(2, len(hook.Entries)) + + hook.Reset() + assert.Nil(hook.LastEntry()) + assert.Equal(0, len(hook.Entries)) + + hook = NewGlobal() + + logrus.Error("Hello error") + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) + +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/json_formatter.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/json_formatter.go new file mode 100644 index 00000000..2ad6dc5c --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/json_formatter.go @@ -0,0 +1,41 @@ +package logrus + +import ( + "encoding/json" + "fmt" +) + +type JSONFormatter struct { + // TimestampFormat sets the format used for marshaling timestamps. + TimestampFormat string +} + +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + data := make(Fields, len(entry.Data)+3) + for k, v := range entry.Data { + switch v := v.(type) { + case error: + // Otherwise errors are ignored by `encoding/json` + // https://github.com/Sirupsen/logrus/issues/137 + data[k] = v.Error() + default: + data[k] = v + } + } + prefixFieldClashes(data) + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat + } + + data["time"] = entry.Time.Format(timestampFormat) + data["msg"] = entry.Message + data["level"] = entry.Level.String() + + serialized, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/json_formatter_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/json_formatter_test.go new file mode 100644 index 00000000..1d708732 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/json_formatter_test.go @@ -0,0 +1,120 @@ +package logrus + +import ( + "encoding/json" + "errors" + + "testing" +) + +func TestErrorNotLost(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("error", errors.New("wild walrus"))) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["error"] != "wild walrus" { + t.Fatal("Error field not set") + } +} + +func TestErrorNotLostOnFieldNotNamedError(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("omg", errors.New("wild walrus"))) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["omg"] != "wild walrus" { + t.Fatal("Error field not set") + } +} + +func TestFieldClashWithTime(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("time", "right now!")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.time"] != "right now!" { + t.Fatal("fields.time not set to original time field") + } + + if entry["time"] != "0001-01-01T00:00:00Z" { + t.Fatal("time field not set to current time, was: ", entry["time"]) + } +} + +func TestFieldClashWithMsg(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("msg", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.msg"] != "something" { + t.Fatal("fields.msg not set to original msg field") + } +} + +func TestFieldClashWithLevel(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.level"] != "something" { + t.Fatal("fields.level not set to original level field") + } +} + +func TestJSONEntryEndsWithNewline(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + if b[len(b)-1] != '\n' { + t.Fatal("Expected JSON log entry to end with a newline") + } +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logger.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logger.go new file mode 100644 index 00000000..2fdb2317 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logger.go @@ -0,0 +1,212 @@ +package logrus + +import ( + "io" + "os" + "sync" +) + +type Logger struct { + // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a + // file, or leave it default which is `os.Stderr`. You can also set this to + // something more adventorous, such as logging to Kafka. + Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging + // levels and log entries. For example, to send errors to an error tracking + // service, log to StatsD or dump the core on fatal errors. + Hooks LevelHooks + // All log entries pass through the formatter before logged to Out. The + // included formatters are `TextFormatter` and `JSONFormatter` for which + // TextFormatter is the default. In development (when a TTY is attached) it + // logs with colors, but to a file it wouldn't. You can easily implement your + // own that implements the `Formatter` interface, see the `README` or included + // formatters for examples. + Formatter Formatter + // The logging level the logger should log at. This is typically (and defaults + // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be + // logged. `logrus.Debug` is useful in + Level Level + // Used to sync writing to the log. + mu sync.Mutex +} + +// Creates a new logger. Configuration should be set by changing `Formatter`, +// `Out` and `Hooks` directly on the default logger instance. You can also just +// instantiate your own: +// +// var log = &Logger{ +// Out: os.Stderr, +// Formatter: new(JSONFormatter), +// Hooks: make(LevelHooks), +// Level: logrus.DebugLevel, +// } +// +// It's recommended to make this a global instance called `log`. +func New() *Logger { + return &Logger{ + Out: os.Stderr, + Formatter: new(TextFormatter), + Hooks: make(LevelHooks), + Level: InfoLevel, + } +} + +// Adds a field to the log entry, note that you it doesn't log until you call +// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. +// If you want multiple fields, use `WithFields`. +func (logger *Logger) WithField(key string, value interface{}) *Entry { + return NewEntry(logger).WithField(key, value) +} + +// Adds a struct of fields to the log entry. All it does is call `WithField` for +// each `Field`. +func (logger *Logger) WithFields(fields Fields) *Entry { + return NewEntry(logger).WithFields(fields) +} + +// Add an error as single field to the log entry. All it does is call +// `WithError` for the given `error`. +func (logger *Logger) WithError(err error) *Entry { + return NewEntry(logger).WithError(err) +} + +func (logger *Logger) Debugf(format string, args ...interface{}) { + if logger.Level >= DebugLevel { + NewEntry(logger).Debugf(format, args...) + } +} + +func (logger *Logger) Infof(format string, args ...interface{}) { + if logger.Level >= InfoLevel { + NewEntry(logger).Infof(format, args...) + } +} + +func (logger *Logger) Printf(format string, args ...interface{}) { + NewEntry(logger).Printf(format, args...) +} + +func (logger *Logger) Warnf(format string, args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnf(format, args...) + } +} + +func (logger *Logger) Warningf(format string, args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnf(format, args...) + } +} + +func (logger *Logger) Errorf(format string, args ...interface{}) { + if logger.Level >= ErrorLevel { + NewEntry(logger).Errorf(format, args...) + } +} + +func (logger *Logger) Fatalf(format string, args ...interface{}) { + if logger.Level >= FatalLevel { + NewEntry(logger).Fatalf(format, args...) + } + os.Exit(1) +} + +func (logger *Logger) Panicf(format string, args ...interface{}) { + if logger.Level >= PanicLevel { + NewEntry(logger).Panicf(format, args...) + } +} + +func (logger *Logger) Debug(args ...interface{}) { + if logger.Level >= DebugLevel { + NewEntry(logger).Debug(args...) + } +} + +func (logger *Logger) Info(args ...interface{}) { + if logger.Level >= InfoLevel { + NewEntry(logger).Info(args...) + } +} + +func (logger *Logger) Print(args ...interface{}) { + NewEntry(logger).Info(args...) +} + +func (logger *Logger) Warn(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warn(args...) + } +} + +func (logger *Logger) Warning(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warn(args...) + } +} + +func (logger *Logger) Error(args ...interface{}) { + if logger.Level >= ErrorLevel { + NewEntry(logger).Error(args...) + } +} + +func (logger *Logger) Fatal(args ...interface{}) { + if logger.Level >= FatalLevel { + NewEntry(logger).Fatal(args...) + } + os.Exit(1) +} + +func (logger *Logger) Panic(args ...interface{}) { + if logger.Level >= PanicLevel { + NewEntry(logger).Panic(args...) + } +} + +func (logger *Logger) Debugln(args ...interface{}) { + if logger.Level >= DebugLevel { + NewEntry(logger).Debugln(args...) + } +} + +func (logger *Logger) Infoln(args ...interface{}) { + if logger.Level >= InfoLevel { + NewEntry(logger).Infoln(args...) + } +} + +func (logger *Logger) Println(args ...interface{}) { + NewEntry(logger).Println(args...) +} + +func (logger *Logger) Warnln(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnln(args...) + } +} + +func (logger *Logger) Warningln(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnln(args...) + } +} + +func (logger *Logger) Errorln(args ...interface{}) { + if logger.Level >= ErrorLevel { + NewEntry(logger).Errorln(args...) + } +} + +func (logger *Logger) Fatalln(args ...interface{}) { + if logger.Level >= FatalLevel { + NewEntry(logger).Fatalln(args...) + } + os.Exit(1) +} + +func (logger *Logger) Panicln(args ...interface{}) { + if logger.Level >= PanicLevel { + NewEntry(logger).Panicln(args...) + } +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logrus.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logrus.go new file mode 100644 index 00000000..1e9670d8 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logrus.go @@ -0,0 +1,109 @@ +package logrus + +import ( + "fmt" + "log" + "strings" +) + +// 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 strings.ToLower(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) +} + +// A constant exposing all logging levels +var AllLevels = []Level{ + PanicLevel, + FatalLevel, + ErrorLevel, + WarnLevel, + InfoLevel, + DebugLevel, +} + +// These are the different logging levels. You can set the logging level to log +// on your instance of logger, obtained with `logrus.New()`. +const ( + // PanicLevel level, highest level of severity. Logs and then calls panic with the + // message passed to Debug, Info, ... + PanicLevel Level = iota + // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the + // logging level is set to Panic. + FatalLevel + // ErrorLevel level. Logs. Used for errors that should definitely be noted. + // Commonly used for hooks to send errors to an error tracking service. + ErrorLevel + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel + // InfoLevel level. General operational entries about what's going on inside the + // application. + InfoLevel + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel +) + +// Won't compile if StdLogger can't be realized by a log.Logger +var ( + _ StdLogger = &log.Logger{} + _ StdLogger = &Entry{} + _ StdLogger = &Logger{} +) + +// StdLogger is what your logrus-enabled library should take, that way +// it'll accept a stdlib logger and a logrus logger. There's no standard +// interface, this is the closest we get, unfortunately. +type StdLogger interface { + Print(...interface{}) + Printf(string, ...interface{}) + Println(...interface{}) + + Fatal(...interface{}) + Fatalf(string, ...interface{}) + Fatalln(...interface{}) + + Panic(...interface{}) + Panicf(string, ...interface{}) + Panicln(...interface{}) +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logrus_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logrus_test.go new file mode 100644 index 00000000..2cf03f2f --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/logrus_test.go @@ -0,0 +1,344 @@ +package logrus + +import ( + "bytes" + "encoding/json" + "strconv" + "strings" + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + log(logger) + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + assertions(fields) +} + +func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { + var buffer bytes.Buffer + + logger := New() + logger.Out = &buffer + logger.Formatter = &TextFormatter{ + DisableColors: true, + } + + log(logger) + + fields := make(map[string]string) + for _, kv := range strings.Split(buffer.String(), " ") { + if !strings.Contains(kv, "=") { + continue + } + kvArr := strings.Split(kv, "=") + key := strings.TrimSpace(kvArr[0]) + val := kvArr[1] + if kvArr[1][0] == '"' { + var err error + val, err = strconv.Unquote(val) + assert.NoError(t, err) + } + fields[key] = val + } + assertions(fields) +} + +func TestPrint(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "info") + }) +} + +func TestInfo(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "info") + }) +} + +func TestWarn(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Warn("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "warning") + }) +} + +func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln("test", "test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test test") + }) +} + +func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln("test", 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test 10") + }) +} + +func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln(10, 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "10 10") + }) +} + +func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln(10, 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "10 10") + }) +} + +func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test", 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test10") + }) +} + +func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test", "test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "testtest") + }) +} + +func TestWithFieldsShouldAllowAssignments(t *testing.T) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + localLog := logger.WithFields(Fields{ + "key1": "value1", + }) + + localLog.WithField("key2", "value2").Info("test") + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + assert.Equal(t, "value2", fields["key2"]) + assert.Equal(t, "value1", fields["key1"]) + + buffer = bytes.Buffer{} + fields = Fields{} + localLog.Info("test") + err = json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + _, ok := fields["key2"] + assert.Equal(t, false, ok) + assert.Equal(t, "value1", fields["key1"]) +} + +func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("msg", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + }) +} + +func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("msg", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["fields.msg"], "hello") + }) +} + +func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("time", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["fields.time"], "hello") + }) +} + +func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("level", 1).Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["level"], "info") + assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only + }) +} + +func TestDefaultFieldsAreNotPrefixed(t *testing.T) { + LogAndAssertText(t, func(log *Logger) { + ll := log.WithField("herp", "derp") + ll.Info("hello") + ll.Info("bye") + }, func(fields map[string]string) { + for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} { + if _, ok := fields[fieldName]; ok { + t.Fatalf("should not have prefixed %q: %v", fieldName, fields) + } + } + }) +} + +func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { + + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + llog := logger.WithField("context", "eating raw fish") + + llog.Info("looks delicious") + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.NoError(t, err, "should have decoded first message") + assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") + assert.Equal(t, fields["msg"], "looks delicious") + assert.Equal(t, fields["context"], "eating raw fish") + + buffer.Reset() + + llog.Warn("omg it is!") + + err = json.Unmarshal(buffer.Bytes(), &fields) + assert.NoError(t, err, "should have decoded second message") + assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") + assert.Equal(t, fields["msg"], "omg it is!") + assert.Equal(t, fields["context"], "eating raw fish") + assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") + +} + +func TestConvertLevelToString(t *testing.T) { + assert.Equal(t, "debug", DebugLevel.String()) + assert.Equal(t, "info", InfoLevel.String()) + assert.Equal(t, "warning", WarnLevel.String()) + assert.Equal(t, "error", ErrorLevel.String()) + assert.Equal(t, "fatal", FatalLevel.String()) + assert.Equal(t, "panic", PanicLevel.String()) +} + +func TestParseLevel(t *testing.T) { + l, err := ParseLevel("panic") + assert.Nil(t, err) + assert.Equal(t, PanicLevel, l) + + l, err = ParseLevel("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("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("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("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("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("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("DEBUG") + assert.Nil(t, err) + assert.Equal(t, DebugLevel, l) + + l, err = ParseLevel("invalid") + assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) +} + +func TestGetSetLevelRace(t *testing.T) { + wg := sync.WaitGroup{} + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + if i%2 == 0 { + SetLevel(InfoLevel) + } else { + GetLevel() + } + }(i) + + } + wg.Wait() +} + +func TestLoggingRace(t *testing.T) { + logger := New() + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + logger.Info("info") + wg.Done() + }() + } + wg.Wait() +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_bsd.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_bsd.go new file mode 100644 index 00000000..71f8d67a --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_bsd.go @@ -0,0 +1,9 @@ +// +build darwin freebsd openbsd netbsd dragonfly + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_linux.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_linux.go new file mode 100644 index 00000000..a2c0b40d --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_linux.go @@ -0,0 +1,12 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TCGETS + +type Termios syscall.Termios diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go new file mode 100644 index 00000000..b343b3a3 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go @@ -0,0 +1,21 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin freebsd openbsd netbsd dragonfly + +package logrus + +import ( + "syscall" + "unsafe" +) + +// IsTerminal returns true if stderr's file descriptor is a terminal. +func IsTerminal() bool { + fd := syscall.Stderr + 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/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_solaris.go new file mode 100644 index 00000000..3e70bf7b --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_solaris.go @@ -0,0 +1,15 @@ +// +build solaris + +package logrus + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal() bool { + _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) + return err == nil +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_windows.go new file mode 100644 index 00000000..0146845d --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/terminal_windows.go @@ -0,0 +1,27 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package logrus + +import ( + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +// IsTerminal returns true if stderr's file descriptor is a terminal. +func IsTerminal() bool { + fd := syscall.Stderr + 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/go/src/gosetup/vendor/github.com/Sirupsen/logrus/text_formatter.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/text_formatter.go new file mode 100644 index 00000000..06ef2023 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/text_formatter.go @@ -0,0 +1,161 @@ +package logrus + +import ( + "bytes" + "fmt" + "runtime" + "sort" + "strings" + "time" +) + +const ( + nocolor = 0 + red = 31 + green = 32 + yellow = 33 + blue = 34 + gray = 37 +) + +var ( + baseTimestamp time.Time + isTerminal bool +) + +func init() { + baseTimestamp = time.Now() + isTerminal = IsTerminal() +} + +func miniTS() int { + return int(time.Since(baseTimestamp) / time.Second) +} + +type TextFormatter struct { + // Set to true to bypass checking for a TTY before outputting colors. + ForceColors bool + + // Force disabling colors. + DisableColors bool + + // Disable timestamp logging. useful when output is redirected to logging + // system that already adds timestamps. + DisableTimestamp bool + + // Enable logging the full timestamp when a TTY is attached instead of just + // the time passed since beginning of execution. + FullTimestamp bool + + // TimestampFormat to use for display when a full timestamp is printed + TimestampFormat string + + // The fields are sorted by default for a consistent output. For applications + // that log extremely frequently and don't use the JSON formatter this may not + // be desired. + DisableSorting bool +} + +func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { + var keys []string = make([]string, 0, len(entry.Data)) + for k := range entry.Data { + keys = append(keys, k) + } + + if !f.DisableSorting { + sort.Strings(keys) + } + + b := &bytes.Buffer{} + + prefixFieldClashes(entry.Data) + + isColorTerminal := isTerminal && (runtime.GOOS != "windows") + isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat + } + if isColored { + f.printColored(b, entry, keys, timestampFormat) + } else { + if !f.DisableTimestamp { + f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) + } + f.appendKeyValue(b, "level", entry.Level.String()) + if entry.Message != "" { + f.appendKeyValue(b, "msg", entry.Message) + } + for _, key := range keys { + f.appendKeyValue(b, key, entry.Data[key]) + } + } + + b.WriteByte('\n') + return b.Bytes(), nil +} + +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { + var levelColor int + switch entry.Level { + case DebugLevel: + levelColor = gray + case WarnLevel: + levelColor = yellow + case ErrorLevel, FatalLevel, PanicLevel: + levelColor = red + default: + levelColor = blue + } + + levelText := strings.ToUpper(entry.Level.String())[0:4] + + if !f.FullTimestamp { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) + } else { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) + } + for _, k := range keys { + v := entry.Data[k] + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) + } +} + +func needsQuoting(text string) bool { + for _, ch := range text { + if !((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '-' || ch == '.') { + return false + } + } + return true +} + +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { + + b.WriteString(key) + b.WriteByte('=') + + switch value := value.(type) { + case string: + if needsQuoting(value) { + b.WriteString(value) + } else { + fmt.Fprintf(b, "%q", value) + } + case error: + errmsg := value.Error() + if needsQuoting(errmsg) { + b.WriteString(errmsg) + } else { + fmt.Fprintf(b, "%q", value) + } + default: + fmt.Fprint(b, value) + } + + b.WriteByte(' ') +} diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/text_formatter_test.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/text_formatter_test.go new file mode 100644 index 00000000..e25a44f6 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/text_formatter_test.go @@ -0,0 +1,61 @@ +package logrus + +import ( + "bytes" + "errors" + "testing" + "time" +) + +func TestQuoting(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + checkQuoting := func(q bool, value interface{}) { + b, _ := tf.Format(WithField("test", value)) + idx := bytes.Index(b, ([]byte)("test=")) + cont := bytes.Contains(b[idx+5:], []byte{'"'}) + if cont != q { + if q { + t.Errorf("quoting expected for: %#v", value) + } else { + t.Errorf("quoting not expected for: %#v", value) + } + } + } + + checkQuoting(false, "abcd") + checkQuoting(false, "v1.0") + checkQuoting(false, "1234567890") + checkQuoting(true, "/foobar") + checkQuoting(true, "x y") + checkQuoting(true, "x,y") + checkQuoting(false, errors.New("invalid")) + checkQuoting(true, errors.New("invalid argument")) +} + +func TestTimestampFormat(t *testing.T) { + checkTimeStr := func(format string) { + customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} + customStr, _ := customFormatter.Format(WithField("test", "test")) + timeStart := bytes.Index(customStr, ([]byte)("time=")) + timeEnd := bytes.Index(customStr, ([]byte)("level=")) + timeStr := customStr[timeStart+5 : timeEnd-1] + if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { + timeStr = timeStr[1 : len(timeStr)-1] + } + if format == "" { + format = time.RFC3339 + } + _, e := time.Parse(format, (string)(timeStr)) + if e != nil { + t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) + } + } + + checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") + checkTimeStr("Mon Jan _2 15:04:05 2006") + checkTimeStr("") +} + +// TODO add tests for sorting etc., this requires a parser for the text +// formatter output. diff --git a/go/src/gosetup/vendor/github.com/Sirupsen/logrus/writer.go b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/writer.go new file mode 100644 index 00000000..1e30b1c7 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/Sirupsen/logrus/writer.go @@ -0,0 +1,31 @@ +package logrus + +import ( + "bufio" + "io" + "runtime" +) + +func (logger *Logger) Writer() *io.PipeWriter { + reader, writer := io.Pipe() + + go logger.writerScanner(reader) + runtime.SetFinalizer(writer, writerFinalizer) + + return writer +} + +func (logger *Logger) writerScanner(reader *io.PipeReader) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + logger.Print(scanner.Text()) + } + if err := scanner.Err(); err != nil { + logger.Errorf("Error while reading from Writer: %s", err) + } + reader.Close() +} + +func writerFinalizer(writer *io.PipeWriter) { + writer.Close() +} diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/CONTRIBUTING.md b/go/src/gosetup/vendor/github.com/docker/go-units/CONTRIBUTING.md new file mode 100644 index 00000000..9ea86d78 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to go-units + +Want to hack on go-units? Awesome! Here are instructions to get you started. + +go-units is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/LICENSE.code b/go/src/gosetup/vendor/github.com/docker/go-units/LICENSE.code new file mode 100644 index 00000000..b55b37bc --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/LICENSE.code @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/LICENSE.docs b/go/src/gosetup/vendor/github.com/docker/go-units/LICENSE.docs new file mode 100644 index 00000000..e26cd4fc --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/MAINTAINERS b/go/src/gosetup/vendor/github.com/docker/go-units/MAINTAINERS new file mode 100644 index 00000000..477be8b2 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/MAINTAINERS @@ -0,0 +1,27 @@ +# go-connections maintainers file +# +# This file describes who runs the docker/go-connections project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "calavera", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + [people.calavera] + Name = "David Calavera" + Email = "david.calavera@gmail.com" + GitHub = "calavera" diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/README.md b/go/src/gosetup/vendor/github.com/docker/go-units/README.md new file mode 100644 index 00000000..3ce4d79d --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/README.md @@ -0,0 +1,18 @@ +[![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) + +# Introduction + +go-units is a library to transform human friendly measurements into machine friendly values. + +## Usage + +See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. + +## Copyright and license + +Copyright © 2015 Docker, Inc. All rights reserved, except as follows. Code +is released under the Apache 2.0 license. The README.md file, and files in the +"docs" folder are licensed under the Creative Commons Attribution 4.0 +International License under the terms and conditions set forth in the file +"LICENSE.docs". You may obtain a duplicate copy of the same license, titled +CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/circle.yml b/go/src/gosetup/vendor/github.com/docker/go-units/circle.yml new file mode 100644 index 00000000..9043b354 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/circle.yml @@ -0,0 +1,11 @@ +dependencies: + post: + # install golint + - go get github.com/golang/lint/golint + +test: + pre: + # run analysis before tests + - go vet ./... + - test -z "$(golint ./... | tee /dev/stderr)" + - test -z "$(gofmt -s -l . | tee /dev/stderr)" diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/duration.go b/go/src/gosetup/vendor/github.com/docker/go-units/duration.go new file mode 100644 index 00000000..c219a8a9 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/duration.go @@ -0,0 +1,33 @@ +// Package units provides helper function to parse and print size and time units +// in human-readable format. +package units + +import ( + "fmt" + "time" +) + +// HumanDuration returns a human-readable approximation of a duration +// (eg. "About a minute", "4 hours ago", etc.). +func HumanDuration(d time.Duration) string { + if seconds := int(d.Seconds()); seconds < 1 { + return "Less than a second" + } else if seconds < 60 { + return fmt.Sprintf("%d seconds", seconds) + } else if minutes := int(d.Minutes()); minutes == 1 { + return "About a minute" + } else if minutes < 60 { + return fmt.Sprintf("%d minutes", minutes) + } else if hours := int(d.Hours()); hours == 1 { + return "About an hour" + } else if hours < 48 { + return fmt.Sprintf("%d hours", hours) + } else if hours < 24*7*2 { + return fmt.Sprintf("%d days", hours/24) + } else if hours < 24*30*3 { + return fmt.Sprintf("%d weeks", hours/24/7) + } else if hours < 24*365*2 { + return fmt.Sprintf("%d months", hours/24/30) + } + return fmt.Sprintf("%d years", int(d.Hours())/24/365) +} diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/duration_test.go b/go/src/gosetup/vendor/github.com/docker/go-units/duration_test.go new file mode 100644 index 00000000..63baa515 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/duration_test.go @@ -0,0 +1,81 @@ +package units + +import ( + "fmt" + "testing" + "time" +) + +func ExampleHumanDuration() { + fmt.Println(HumanDuration(450 * time.Millisecond)) + fmt.Println(HumanDuration(47 * time.Second)) + fmt.Println(HumanDuration(1 * time.Minute)) + fmt.Println(HumanDuration(3 * time.Minute)) + fmt.Println(HumanDuration(35 * time.Minute)) + fmt.Println(HumanDuration(35*time.Minute + 40*time.Second)) + fmt.Println(HumanDuration(1 * time.Hour)) + fmt.Println(HumanDuration(1*time.Hour + 45*time.Minute)) + fmt.Println(HumanDuration(3 * time.Hour)) + fmt.Println(HumanDuration(3*time.Hour + 59*time.Minute)) + fmt.Println(HumanDuration(3*time.Hour + 60*time.Minute)) + fmt.Println(HumanDuration(24 * time.Hour)) + fmt.Println(HumanDuration(24*time.Hour + 12*time.Hour)) + fmt.Println(HumanDuration(2 * 24 * time.Hour)) + fmt.Println(HumanDuration(7 * 24 * time.Hour)) + fmt.Println(HumanDuration(13*24*time.Hour + 5*time.Hour)) + fmt.Println(HumanDuration(2 * 7 * 24 * time.Hour)) + fmt.Println(HumanDuration(2*7*24*time.Hour + 4*24*time.Hour)) + fmt.Println(HumanDuration(3 * 7 * 24 * time.Hour)) + fmt.Println(HumanDuration(4 * 7 * 24 * time.Hour)) + fmt.Println(HumanDuration(4*7*24*time.Hour + 3*24*time.Hour)) + fmt.Println(HumanDuration(1 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(1*30*24*time.Hour + 2*7*24*time.Hour)) + fmt.Println(HumanDuration(2 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(3*30*24*time.Hour + 1*7*24*time.Hour)) + fmt.Println(HumanDuration(5*30*24*time.Hour + 2*7*24*time.Hour)) + fmt.Println(HumanDuration(13 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(23 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(24 * 30 * 24 * time.Hour)) + fmt.Println(HumanDuration(24*30*24*time.Hour + 2*7*24*time.Hour)) + fmt.Println(HumanDuration(3*365*24*time.Hour + 2*30*24*time.Hour)) +} + +func TestHumanDuration(t *testing.T) { + // Useful duration abstractions + day := 24 * time.Hour + week := 7 * day + month := 30 * day + year := 365 * day + + assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond)) + assertEquals(t, "47 seconds", HumanDuration(47*time.Second)) + assertEquals(t, "About a minute", HumanDuration(1*time.Minute)) + assertEquals(t, "3 minutes", HumanDuration(3*time.Minute)) + assertEquals(t, "35 minutes", HumanDuration(35*time.Minute)) + assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second)) + assertEquals(t, "About an hour", HumanDuration(1*time.Hour)) + assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute)) + assertEquals(t, "3 hours", HumanDuration(3*time.Hour)) + assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute)) + assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute)) + assertEquals(t, "24 hours", HumanDuration(24*time.Hour)) + assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour)) + assertEquals(t, "2 days", HumanDuration(2*day)) + assertEquals(t, "7 days", HumanDuration(7*day)) + assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour)) + assertEquals(t, "2 weeks", HumanDuration(2*week)) + assertEquals(t, "2 weeks", HumanDuration(2*week+4*day)) + assertEquals(t, "3 weeks", HumanDuration(3*week)) + assertEquals(t, "4 weeks", HumanDuration(4*week)) + assertEquals(t, "4 weeks", HumanDuration(4*week+3*day)) + assertEquals(t, "4 weeks", HumanDuration(1*month)) + assertEquals(t, "6 weeks", HumanDuration(1*month+2*week)) + assertEquals(t, "8 weeks", HumanDuration(2*month)) + assertEquals(t, "3 months", HumanDuration(3*month+1*week)) + assertEquals(t, "5 months", HumanDuration(5*month+2*week)) + assertEquals(t, "13 months", HumanDuration(13*month)) + assertEquals(t, "23 months", HumanDuration(23*month)) + assertEquals(t, "24 months", HumanDuration(24*month)) + assertEquals(t, "2 years", HumanDuration(24*month+2*week)) + assertEquals(t, "3 years", HumanDuration(3*year+2*month)) +} diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/size.go b/go/src/gosetup/vendor/github.com/docker/go-units/size.go new file mode 100644 index 00000000..989edd29 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/size.go @@ -0,0 +1,95 @@ +package units + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +// See: http://en.wikipedia.org/wiki/Binary_prefix +const ( + // Decimal + + KB = 1000 + MB = 1000 * KB + GB = 1000 * MB + TB = 1000 * GB + PB = 1000 * TB + + // Binary + + KiB = 1024 + MiB = 1024 * KiB + GiB = 1024 * MiB + TiB = 1024 * GiB + PiB = 1024 * TiB +) + +type unitMap map[string]int64 + +var ( + decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} + binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`) +) + +var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} +var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + +// CustomSize returns a human-readable approximation of a size +// using custom format. +func CustomSize(format string, size float64, base float64, _map []string) string { + i := 0 + for size >= base { + size = size / base + i++ + } + return fmt.Sprintf(format, size, _map[i]) +} + +// HumanSize returns a human-readable approximation of a size +// capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). +func HumanSize(size float64) string { + return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs) +} + +// BytesSize returns a human-readable size in bytes, kibibytes, +// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). +func BytesSize(size float64) string { + return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) +} + +// FromHumanSize returns an integer from a human-readable specification of a +// size using SI standard (eg. "44kB", "17MB"). +func FromHumanSize(size string) (int64, error) { + return parseSize(size, decimalMap) +} + +// RAMInBytes parses a human-readable string representing an amount of RAM +// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and +// returns the number of bytes, or -1 if the string is unparseable. +// Units are case-insensitive, and the 'b' suffix is optional. +func RAMInBytes(size string) (int64, error) { + return parseSize(size, binaryMap) +} + +// Parses the human-readable size string into the amount it represents. +func parseSize(sizeStr string, uMap unitMap) (int64, error) { + matches := sizeRegex.FindStringSubmatch(sizeStr) + if len(matches) != 4 { + return -1, fmt.Errorf("invalid size: '%s'", sizeStr) + } + + size, err := strconv.ParseFloat(matches[1], 64) + if err != nil { + return -1, err + } + + unitPrefix := strings.ToLower(matches[3]) + if mul, ok := uMap[unitPrefix]; ok { + size *= float64(mul) + } + + return int64(size), nil +} diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/size_test.go b/go/src/gosetup/vendor/github.com/docker/go-units/size_test.go new file mode 100644 index 00000000..003bc89a --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/size_test.go @@ -0,0 +1,163 @@ +package units + +import ( + "fmt" + "reflect" + "runtime" + "strings" + "testing" +) + +func ExampleBytesSize() { + fmt.Println(BytesSize(1024)) + fmt.Println(BytesSize(1024 * 1024)) + fmt.Println(BytesSize(1048576)) + fmt.Println(BytesSize(2 * MiB)) + fmt.Println(BytesSize(3.42 * GiB)) + fmt.Println(BytesSize(5.372 * TiB)) + fmt.Println(BytesSize(2.22 * PiB)) +} + +func ExampleHumanSize() { + fmt.Println(HumanSize(1000)) + fmt.Println(HumanSize(1024)) + fmt.Println(HumanSize(1000000)) + fmt.Println(HumanSize(1048576)) + fmt.Println(HumanSize(2 * MB)) + fmt.Println(HumanSize(float64(3.42 * GB))) + fmt.Println(HumanSize(float64(5.372 * TB))) + fmt.Println(HumanSize(float64(2.22 * PB))) +} + +func ExampleFromHumanSize() { + fmt.Println(FromHumanSize("32")) + fmt.Println(FromHumanSize("32b")) + fmt.Println(FromHumanSize("32B")) + fmt.Println(FromHumanSize("32k")) + fmt.Println(FromHumanSize("32K")) + fmt.Println(FromHumanSize("32kb")) + fmt.Println(FromHumanSize("32Kb")) + fmt.Println(FromHumanSize("32Mb")) + fmt.Println(FromHumanSize("32Gb")) + fmt.Println(FromHumanSize("32Tb")) + fmt.Println(FromHumanSize("32Pb")) +} + +func ExampleRAMInBytes() { + fmt.Println(RAMInBytes("32")) + fmt.Println(RAMInBytes("32b")) + fmt.Println(RAMInBytes("32B")) + fmt.Println(RAMInBytes("32k")) + fmt.Println(RAMInBytes("32K")) + fmt.Println(RAMInBytes("32kb")) + fmt.Println(RAMInBytes("32Kb")) + fmt.Println(RAMInBytes("32Mb")) + fmt.Println(RAMInBytes("32Gb")) + fmt.Println(RAMInBytes("32Tb")) + fmt.Println(RAMInBytes("32Pb")) + fmt.Println(RAMInBytes("32PB")) + fmt.Println(RAMInBytes("32P")) +} + +func TestBytesSize(t *testing.T) { + assertEquals(t, "1 KiB", BytesSize(1024)) + assertEquals(t, "1 MiB", BytesSize(1024*1024)) + assertEquals(t, "1 MiB", BytesSize(1048576)) + assertEquals(t, "2 MiB", BytesSize(2*MiB)) + assertEquals(t, "3.42 GiB", BytesSize(3.42*GiB)) + assertEquals(t, "5.372 TiB", BytesSize(5.372*TiB)) + assertEquals(t, "2.22 PiB", BytesSize(2.22*PiB)) +} + +func TestHumanSize(t *testing.T) { + assertEquals(t, "1 kB", HumanSize(1000)) + assertEquals(t, "1.024 kB", HumanSize(1024)) + assertEquals(t, "1 MB", HumanSize(1000000)) + assertEquals(t, "1.049 MB", HumanSize(1048576)) + assertEquals(t, "2 MB", HumanSize(2*MB)) + assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB))) + assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB))) + assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB))) +} + +func TestFromHumanSize(t *testing.T) { + assertSuccessEquals(t, 32, FromHumanSize, "32") + assertSuccessEquals(t, 32, FromHumanSize, "32b") + assertSuccessEquals(t, 32, FromHumanSize, "32B") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32k") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32K") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb") + assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb") + assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb") + assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb") + assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb") + + assertSuccessEquals(t, 32.5*KB, FromHumanSize, "32.5kB") + assertSuccessEquals(t, 32.5*KB, FromHumanSize, "32.5 kB") + assertSuccessEquals(t, 32, FromHumanSize, "32.5 B") + + assertError(t, FromHumanSize, "") + assertError(t, FromHumanSize, "hello") + assertError(t, FromHumanSize, "-32") + assertError(t, FromHumanSize, ".3kB") + assertError(t, FromHumanSize, " 32 ") + assertError(t, FromHumanSize, "32m b") + assertError(t, FromHumanSize, "32bm") +} + +func TestRAMInBytes(t *testing.T) { + assertSuccessEquals(t, 32, RAMInBytes, "32") + assertSuccessEquals(t, 32, RAMInBytes, "32b") + assertSuccessEquals(t, 32, RAMInBytes, "32B") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb") + assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb") + assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb") + assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb") + assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb") + assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB") + assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P") + + assertSuccessEquals(t, 32, RAMInBytes, "32.3") + tmp := 32.3 * MiB + assertSuccessEquals(t, int64(tmp), RAMInBytes, "32.3 mb") + + assertError(t, RAMInBytes, "") + assertError(t, RAMInBytes, "hello") + assertError(t, RAMInBytes, "-32") + assertError(t, RAMInBytes, " 32 ") + assertError(t, RAMInBytes, "32m b") + assertError(t, RAMInBytes, "32bm") +} + +func assertEquals(t *testing.T, expected, actual interface{}) { + if expected != actual { + t.Errorf("Expected '%v' but got '%v'", expected, actual) + } +} + +// func that maps to the parse function signatures as testing abstraction +type parseFn func(string) (int64, error) + +// Define 'String()' for pretty-print +func (fn parseFn) String() string { + fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() + return fnName[strings.LastIndex(fnName, ".")+1:] +} + +func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) { + res, err := fn(arg) + if err != nil || res != expected { + t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err) + } +} + +func assertError(t *testing.T, fn parseFn, arg string) { + res, err := fn(arg) + if err == nil && res != -1 { + t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res) + } +} diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/ulimit.go b/go/src/gosetup/vendor/github.com/docker/go-units/ulimit.go new file mode 100644 index 00000000..5ac7fd82 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/ulimit.go @@ -0,0 +1,118 @@ +package units + +import ( + "fmt" + "strconv" + "strings" +) + +// Ulimit is a human friendly version of Rlimit. +type Ulimit struct { + Name string + Hard int64 + Soft int64 +} + +// Rlimit specifies the resource limits, such as max open files. +type Rlimit struct { + Type int `json:"type,omitempty"` + Hard uint64 `json:"hard,omitempty"` + Soft uint64 `json:"soft,omitempty"` +} + +const ( + // magic numbers for making the syscall + // some of these are defined in the syscall package, but not all. + // Also since Windows client doesn't get access to the syscall package, need to + // define these here + rlimitAs = 9 + rlimitCore = 4 + rlimitCPU = 0 + rlimitData = 2 + rlimitFsize = 1 + rlimitLocks = 10 + rlimitMemlock = 8 + rlimitMsgqueue = 12 + rlimitNice = 13 + rlimitNofile = 7 + rlimitNproc = 6 + rlimitRss = 5 + rlimitRtprio = 14 + rlimitRttime = 15 + rlimitSigpending = 11 + rlimitStack = 3 +) + +var ulimitNameMapping = map[string]int{ + //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. + "core": rlimitCore, + "cpu": rlimitCPU, + "data": rlimitData, + "fsize": rlimitFsize, + "locks": rlimitLocks, + "memlock": rlimitMemlock, + "msgqueue": rlimitMsgqueue, + "nice": rlimitNice, + "nofile": rlimitNofile, + "nproc": rlimitNproc, + "rss": rlimitRss, + "rtprio": rlimitRtprio, + "rttime": rlimitRttime, + "sigpending": rlimitSigpending, + "stack": rlimitStack, +} + +// ParseUlimit parses and returns a Ulimit from the specified string. +func ParseUlimit(val string) (*Ulimit, error) { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid ulimit argument: %s", val) + } + + if _, exists := ulimitNameMapping[parts[0]]; !exists { + return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) + } + + var ( + soft int64 + hard = &soft // default to soft in case no hard was set + temp int64 + err error + ) + switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { + case 2: + temp, err = strconv.ParseInt(limitVals[1], 10, 64) + if err != nil { + return nil, err + } + hard = &temp + fallthrough + case 1: + soft, err = strconv.ParseInt(limitVals[0], 10, 64) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) + } + + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } + + return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil +} + +// GetRlimit returns the RLimit corresponding to Ulimit. +func (u *Ulimit) GetRlimit() (*Rlimit, error) { + t, exists := ulimitNameMapping[u.Name] + if !exists { + return nil, fmt.Errorf("invalid ulimit name %s", u.Name) + } + + return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil +} + +func (u *Ulimit) String() string { + return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) +} diff --git a/go/src/gosetup/vendor/github.com/docker/go-units/ulimit_test.go b/go/src/gosetup/vendor/github.com/docker/go-units/ulimit_test.go new file mode 100644 index 00000000..3e7f10fc --- /dev/null +++ b/go/src/gosetup/vendor/github.com/docker/go-units/ulimit_test.go @@ -0,0 +1,74 @@ +package units + +import ( + "fmt" + "strconv" + "testing" +) + +func ExampleParseUlimit() { + fmt.Println(ParseUlimit("nofile=512:1024")) + fmt.Println(ParseUlimit("nofile=1024")) + fmt.Println(ParseUlimit("cpu=2:4")) + fmt.Println(ParseUlimit("cpu=6")) +} + +func TestParseUlimitValid(t *testing.T) { + u1 := &Ulimit{"nofile", 1024, 512} + if u2, _ := ParseUlimit("nofile=512:1024"); *u1 != *u2 { + t.Fatalf("expected %q, but got %q", u1, u2) + } +} + +func TestParseUlimitInvalidLimitType(t *testing.T) { + if _, err := ParseUlimit("notarealtype=1024:1024"); err == nil { + t.Fatalf("expected error on invalid ulimit type") + } +} + +func TestParseUlimitBadFormat(t *testing.T) { + if _, err := ParseUlimit("nofile:1024:1024"); err == nil { + t.Fatal("expected error on bad syntax") + } + + if _, err := ParseUlimit("nofile"); err == nil { + t.Fatal("expected error on bad syntax") + } + + if _, err := ParseUlimit("nofile="); err == nil { + t.Fatal("expected error on bad syntax") + } + if _, err := ParseUlimit("nofile=:"); err == nil { + t.Fatal("expected error on bad syntax") + } + if _, err := ParseUlimit("nofile=:1024"); err == nil { + t.Fatal("expected error on bad syntax") + } +} + +func TestParseUlimitHardLessThanSoft(t *testing.T) { + if _, err := ParseUlimit("nofile=1024:1"); err == nil { + t.Fatal("expected error on hard limit less than soft limit") + } +} + +func TestParseUlimitInvalidValueType(t *testing.T) { + if _, err := ParseUlimit("nofile=asdf"); err == nil { + t.Fatal("expected error on bad value type, but got no error") + } else if _, ok := err.(*strconv.NumError); !ok { + t.Fatalf("expected error on bad value type, but got `%s`", err) + } + + if _, err := ParseUlimit("nofile=1024:asdf"); err == nil { + t.Fatal("expected error on bad value type, but got no error") + } else if _, ok := err.(*strconv.NumError); !ok { + t.Fatalf("expected error on bad value type, but got `%s`", err) + } +} + +func TestUlimitStringOutput(t *testing.T) { + u := &Ulimit{"nofile", 1024, 512} + if s := u.String(); s != "nofile=512:1024" { + t.Fatal("expected String to return nofile=512:1024, but got", s) + } +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/LICENSE b/go/src/gosetup/vendor/github.com/samalba/dockerclient/LICENSE new file mode 100644 index 00000000..00e1edb9 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Sam Alba + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/README.md b/go/src/gosetup/vendor/github.com/samalba/dockerclient/README.md new file mode 100644 index 00000000..6cad9bd3 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/README.md @@ -0,0 +1,98 @@ +Docker client library in Go +=========================== +[![GoDoc](http://godoc.org/github.com/samalba/dockerclient?status.png)](http://godoc.org/github.com/samalba/dockerclient) + +Well maintained docker client library. + +# How to use it? + +Here is an example showing how to use it: + +```go +package main + +import ( + "github.com/samalba/dockerclient" + "log" + "time" + "os" +) + +// Callback used to listen to Docker's events +func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) { + log.Printf("Received event: %#v\n", *event) +} + +func main() { + // Init the client + docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil) + + // Get only running containers + containers, err := docker.ListContainers(false, false, "") + if err != nil { + log.Fatal(err) + } + for _, c := range containers { + log.Println(c.Id, c.Names) + } + + // Inspect the first container returned + if len(containers) > 0 { + id := containers[0].Id + info, _ := docker.InspectContainer(id) + log.Println(info) + } + + // Build a docker image + // some.tar contains the build context (Dockerfile any any files it needs to add/copy) + dockerBuildContext, err := os.Open("some.tar") + defer dockerBuildContext.Close() + buildImageConfig := &dockerclient.BuildImage{ + Context: dockerBuildContext, + RepoName: "your_image_name", + SuppressOutput: false, + } + reader, err := docker.BuildImage(buildImageConfig) + if err != nil { + log.Fatal(err) + } + + // Create a container + containerConfig := &dockerclient.ContainerConfig{ + Image: "ubuntu:14.04", + Cmd: []string{"bash"}, + AttachStdin: true, + Tty: true} + containerId, err := docker.CreateContainer(containerConfig, "foobar", nil) + if err != nil { + log.Fatal(err) + } + + // Start the container + hostConfig := &dockerclient.HostConfig{} + err = docker.StartContainer(containerId, hostConfig) + if err != nil { + log.Fatal(err) + } + + // Stop the container (with 5 seconds timeout) + docker.StopContainer(containerId, 5) + + // Listen to events + docker.StartMonitorEvents(eventCallback, nil) + + // Hold the execution to look at the events coming + time.Sleep(3600 * time.Second) +} +``` + +# Maintainers + +List of people you can ping for feedback on Pull Requests or any questions. + +- [Sam Alba](https://github.com/samalba) +- [Michael Crosby](https://github.com/crosbymichael) +- [Andrea Luzzardi](https://github.com/aluzzardi) +- [Victor Vieux](https://github.com/vieux) +- [Evan Hazlett](https://github.com/ehazlett) +- [Donald Huang](https://github.com/donhcd) diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/auth.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/auth.go new file mode 100644 index 00000000..7c0a67f7 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/auth.go @@ -0,0 +1,39 @@ +package dockerclient + +import ( + "bytes" + "encoding/base64" + "encoding/json" +) + +// AuthConfig hold parameters for authenticating with the docker registry +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Email string `json:"email,omitempty"` + RegistryToken string `json:"registrytoken,omitempty"` +} + +// encode the auth configuration struct into base64 for the X-Registry-Auth header +func (c *AuthConfig) encode() (string, error) { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(c); err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(buf.Bytes()), nil +} + +// ConfigFile holds parameters for authenticating during a BuildImage request +type ConfigFile struct { + Configs map[string]AuthConfig `json:"configs,omitempty"` + rootPath string +} + +// encode the configuration struct into base64 for the X-Registry-Config header +func (c *ConfigFile) encode() (string, error) { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(c); err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(buf.Bytes()), nil +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/auth_test.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/auth_test.go new file mode 100755 index 00000000..99801b22 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/auth_test.go @@ -0,0 +1,15 @@ +package dockerclient + +import ( + "testing" +) + +func TestAuthEncode(t *testing.T) { + a := AuthConfig{Username: "foo", Password: "password", Email: "bar@baz.com"} + expected := "eyJ1c2VybmFtZSI6ImZvbyIsInBhc3N3b3JkIjoicGFzc3dvcmQiLCJlbWFpbCI6ImJhckBiYXouY29tIn0K" + got, _ := a.encode() + + if expected != got { + t.Errorf("testAuthEncode failed. Expected [%s] got [%s]", expected, got) + } +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/dockerclient.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/dockerclient.go new file mode 100644 index 00000000..51a00b29 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/dockerclient.go @@ -0,0 +1,1001 @@ +package dockerclient + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + "sync/atomic" + "time" +) + +var _ Client = (*DockerClient)(nil) + +const ( + // APIVersion is currently hardcoded to v1.15 + // TODO: bump the API version or allow users to choose which API version to + // use the client with. The current value does not make sense for many + // methods, such as ContainerStats, StartMonitorStats, and StopAllMonitorStats + // (v1.17) and + // ListVolumes, {Remove,Create}Volume, ListNetworks, + // {Inspect,Create,Connect,Disconnect,Remove}Network (v1.21) + APIVersion = "v1.15" +) + +var ( + ErrImageNotFound = errors.New("Image not found") + ErrNotFound = errors.New("Not found") + ErrConnectionRefused = errors.New("Cannot connect to the docker engine endpoint") + + defaultTimeout = 30 * time.Second +) + +type DockerClient struct { + URL *url.URL + HTTPClient *http.Client + TLSConfig *tls.Config + monitorStats int32 + eventStopChan chan (struct{}) +} + +type Error struct { + StatusCode int + Status string + msg string +} + +func (e Error) Error() string { + return fmt.Sprintf("%s: %s", e.Status, e.msg) +} + +func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) { + return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout), nil) +} + +func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration, setUserTimeout tcpFunc) (*DockerClient, error) { + u, err := url.Parse(daemonUrl) + if err != nil { + return nil, err + } + if u.Scheme == "" || u.Scheme == "tcp" { + if tlsConfig == nil { + u.Scheme = "http" + } else { + u.Scheme = "https" + } + } + httpClient := newHTTPClient(u, tlsConfig, timeout, setUserTimeout) + return &DockerClient{u, httpClient, tlsConfig, 0, nil}, nil +} + +func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) { + b := bytes.NewBuffer(body) + + reader, err := client.doStreamRequest(method, path, b, headers) + if err != nil { + return nil, err + } + + defer reader.Close() + data, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + return data, nil +} + +func (client *DockerClient) doStreamRequest(method string, path string, in io.Reader, headers map[string]string) (io.ReadCloser, error) { + if (method == "POST" || method == "PUT") && in == nil { + in = bytes.NewReader(nil) + } + req, err := http.NewRequest(method, client.URL.String()+path, in) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + if headers != nil { + for header, value := range headers { + req.Header.Add(header, value) + } + } + resp, err := client.HTTPClient.Do(req) + if err != nil { + if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil { + return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err) + } + if strings.Contains(err.Error(), "connection refused") { + return nil, ErrConnectionRefused + } + return nil, err + } + if resp.StatusCode == 404 { + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, ErrNotFound + } + if len(data) > 0 { + // check if is image not found error + if strings.Index(string(data), "No such image") != -1 { + return nil, ErrImageNotFound + } + return nil, errors.New(string(data)) + } + return nil, ErrNotFound + } + if resp.StatusCode >= 400 { + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)} + } + + return resp.Body, nil +} + +func (client *DockerClient) Info() (*Info, error) { + uri := fmt.Sprintf("/%s/info", APIVersion) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + ret := &Info{} + err = json.Unmarshal(data, &ret) + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *DockerClient) ListContainers(all bool, size bool, filters string) ([]Container, error) { + argAll := 0 + if all == true { + argAll = 1 + } + showSize := 0 + if size == true { + showSize = 1 + } + uri := fmt.Sprintf("/%s/containers/json?all=%d&size=%d", APIVersion, argAll, showSize) + + if filters != "" { + uri += "&filters=" + filters + } + + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + ret := []Container{} + err = json.Unmarshal(data, &ret) + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) { + uri := fmt.Sprintf("/%s/containers/%s/json", APIVersion, id) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + info := &ContainerInfo{} + err = json.Unmarshal(data, info) + if err != nil { + return nil, err + } + return info, nil +} + +func (client *DockerClient) CreateContainer(config *ContainerConfig, name string, auth *AuthConfig) (string, error) { + data, err := json.Marshal(config) + if err != nil { + return "", err + } + uri := fmt.Sprintf("/%s/containers/create", APIVersion) + if name != "" { + v := url.Values{} + v.Set("name", name) + uri = fmt.Sprintf("%s?%s", uri, v.Encode()) + } + headers := map[string]string{} + if auth != nil { + encoded_auth, err := auth.encode() + if err != nil { + return "", err + } + headers["X-Registry-Auth"] = encoded_auth + } + data, err = client.doRequest("POST", uri, data, headers) + if err != nil { + return "", err + } + result := &RespContainersCreate{} + err = json.Unmarshal(data, result) + if err != nil { + return "", fmt.Errorf(string(data)) + } + return result.Id, nil +} + +func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) { + v := url.Values{} + v.Add("follow", strconv.FormatBool(options.Follow)) + v.Add("stdout", strconv.FormatBool(options.Stdout)) + v.Add("stderr", strconv.FormatBool(options.Stderr)) + v.Add("timestamps", strconv.FormatBool(options.Timestamps)) + if options.Tail > 0 { + v.Add("tail", strconv.FormatInt(options.Tail, 10)) + } + + uri := fmt.Sprintf("/%s/containers/%s/logs?%s", APIVersion, id, v.Encode()) + req, err := http.NewRequest("GET", client.URL.String()+uri, nil) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + resp, err := client.HTTPClient.Do(req) + if err != nil { + return nil, err + } + return resp.Body, nil +} + +func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, error) { + uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + changes := []*ContainerChanges{} + err = json.Unmarshal(data, &changes) + if err != nil { + return nil, err + } + return changes, nil +} + +func (client *DockerClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error) { + uri := fmt.Sprintf("/%s/containers/%s/stats", APIVersion, id) + resp, err := client.HTTPClient.Get(client.URL.String() + uri) + if err != nil { + return nil, err + } + + decode := func(decoder *json.Decoder) decodingResult { + var containerStats Stats + if err := decoder.Decode(&containerStats); err != nil { + return decodingResult{err: err} + } else { + return decodingResult{result: containerStats} + } + } + decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan) + statsOrErrorChan := make(chan StatsOrError) + go func() { + for decodingResult := range decodingResultChan { + stats, _ := decodingResult.result.(Stats) + statsOrErrorChan <- StatsOrError{ + Stats: stats, + Error: decodingResult.err, + } + } + close(statsOrErrorChan) + }() + return statsOrErrorChan, nil +} + +func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult { + resultChan := make(chan decodingResult) + + go func() { + decodeChan := make(chan decodingResult) + + go func() { + decoder := json.NewDecoder(stream) + for { + decodeResult := decode(decoder) + decodeChan <- decodeResult + if decodeResult.err != nil { + close(decodeChan) + return + } + } + }() + + defer close(resultChan) + + for { + select { + case <-stopChan: + stream.Close() + for range decodeChan { + } + return + case decodeResult := <-decodeChan: + resultChan <- decodeResult + if decodeResult.err != nil { + stream.Close() + return + } + } + } + + }() + + return resultChan +} + +func (client *DockerClient) ExecCreate(config *ExecConfig) (string, error) { + data, err := json.Marshal(config) + if err != nil { + return "", err + } + uri := fmt.Sprintf("/%s/containers/%s/exec", APIVersion, config.Container) + resp, err := client.doRequest("POST", uri, data, nil) + if err != nil { + return "", err + } + var createExecResp struct { + Id string + } + if err = json.Unmarshal(resp, &createExecResp); err != nil { + return "", err + } + return createExecResp.Id, nil +} + +func (client *DockerClient) ExecStart(id string, config *ExecConfig) error { + data, err := json.Marshal(config) + if err != nil { + return err + } + + uri := fmt.Sprintf("/%s/exec/%s/start", APIVersion, id) + if _, err := client.doRequest("POST", uri, data, nil); err != nil { + return err + } + + return nil +} + +func (client *DockerClient) ExecResize(id string, width, height int) error { + v := url.Values{} + + w := strconv.Itoa(width) + h := strconv.Itoa(height) + + v.Set("w", w) + v.Set("h", h) + + uri := fmt.Sprintf("/%s/exec/%s/resize?%s", APIVersion, id, v.Encode()) + if _, err := client.doRequest("POST", client.URL.String()+uri, nil, nil); err != nil { + return err + } + + return nil +} + +func (client *DockerClient) AttachContainer(id string, options *AttachOptions) (io.ReadCloser, error) { + v := url.Values{} + if options != nil { + if options.Logs { + v.Set("logs", "1") + } + if options.Stream { + v.Set("stream", "1") + } + if options.Stdin { + v.Set("stdin", "1") + } + if options.Stdout { + v.Set("stdout", "1") + } + if options.Stderr { + v.Set("stderr", "1") + } + } + uri := fmt.Sprintf("/%s/containers/%s/attach?%s", APIVersion, id, v.Encode()) + return client.doStreamRequest("POST", uri, nil, nil) +} + +func (client *DockerClient) StartContainer(id string, config *HostConfig) error { + data, err := json.Marshal(config) + if err != nil { + return err + } + uri := fmt.Sprintf("/%s/containers/%s/start", APIVersion, id) + _, err = client.doRequest("POST", uri, data, nil) + if err != nil { + return err + } + return nil +} + +func (client *DockerClient) StopContainer(id string, timeout int) error { + uri := fmt.Sprintf("/%s/containers/%s/stop?t=%d", APIVersion, id, timeout) + _, err := client.doRequest("POST", uri, nil, nil) + if err != nil { + return err + } + return nil +} + +func (client *DockerClient) RestartContainer(id string, timeout int) error { + uri := fmt.Sprintf("/%s/containers/%s/restart?t=%d", APIVersion, id, timeout) + _, err := client.doRequest("POST", uri, nil, nil) + if err != nil { + return err + } + return nil +} + +func (client *DockerClient) KillContainer(id, signal string) error { + uri := fmt.Sprintf("/%s/containers/%s/kill?signal=%s", APIVersion, id, signal) + _, err := client.doRequest("POST", uri, nil, nil) + if err != nil { + return err + } + return nil +} + +func (client *DockerClient) Wait(id string) <-chan WaitResult { + ch := make(chan WaitResult) + uri := fmt.Sprintf("/%s/containers/%s/wait", APIVersion, id) + + go func() { + data, err := client.doRequest("POST", uri, nil, nil) + if err != nil { + ch <- WaitResult{ExitCode: -1, Error: err} + return + } + + var result struct { + StatusCode int `json:"StatusCode"` + } + err = json.Unmarshal(data, &result) + ch <- WaitResult{ExitCode: result.StatusCode, Error: err} + }() + return ch +} + +func (client *DockerClient) MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) { + v := url.Values{} + if options != nil { + if options.Since != 0 { + v.Add("since", strconv.Itoa(options.Since)) + } + if options.Until != 0 { + v.Add("until", strconv.Itoa(options.Until)) + } + if options.Filters != nil { + filterMap := make(map[string][]string) + if len(options.Filters.Event) > 0 { + filterMap["event"] = []string{options.Filters.Event} + } + if len(options.Filters.Image) > 0 { + filterMap["image"] = []string{options.Filters.Image} + } + if len(options.Filters.Container) > 0 { + filterMap["container"] = []string{options.Filters.Container} + } + if len(filterMap) > 0 { + filterJSONBytes, err := json.Marshal(filterMap) + if err != nil { + return nil, err + } + v.Add("filters", string(filterJSONBytes)) + } + } + } + uri := fmt.Sprintf("%s/%s/events?%s", client.URL.String(), APIVersion, v.Encode()) + resp, err := client.HTTPClient.Get(uri) + if err != nil { + return nil, err + } + + decode := func(decoder *json.Decoder) decodingResult { + var event Event + if err := decoder.Decode(&event); err != nil { + return decodingResult{err: err} + } else { + return decodingResult{result: event} + } + } + decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan) + eventOrErrorChan := make(chan EventOrError) + go func() { + for decodingResult := range decodingResultChan { + event, _ := decodingResult.result.(Event) + eventOrErrorChan <- EventOrError{ + Event: event, + Error: decodingResult.err, + } + } + close(eventOrErrorChan) + }() + return eventOrErrorChan, nil +} + +func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) { + client.eventStopChan = make(chan struct{}) + + go func() { + eventErrChan, err := client.MonitorEvents(nil, client.eventStopChan) + if err != nil { + if ec != nil { + ec <- err + } + return + } + + for e := range eventErrChan { + if e.Error != nil { + if ec != nil { + ec <- e.Error + } + return + } + cb(&e.Event, ec, args...) + } + }() +} + +func (client *DockerClient) StopAllMonitorEvents() { + if client.eventStopChan == nil { + return + } + close(client.eventStopChan) +} + +func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) { + atomic.StoreInt32(&client.monitorStats, 1) + go client.getStats(id, cb, ec, args...) +} + +func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) { + uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id) + resp, err := client.HTTPClient.Get(uri) + if err != nil { + ec <- err + return + } + defer resp.Body.Close() + + dec := json.NewDecoder(resp.Body) + for atomic.LoadInt32(&client.monitorStats) > 0 { + var stats *Stats + if err := dec.Decode(&stats); err != nil { + ec <- err + return + } + cb(id, stats, ec, args...) + } +} + +func (client *DockerClient) StopAllMonitorStats() { + atomic.StoreInt32(&client.monitorStats, 0) +} + +func (client *DockerClient) TagImage(nameOrID string, repo string, tag string, force bool) error { + v := url.Values{} + v.Set("repo", repo) + v.Set("tag", tag) + if force { + v.Set("force", "1") + } + uri := fmt.Sprintf("/%s/images/%s/tag?%s", APIVersion, nameOrID, v.Encode()) + if _, err := client.doRequest("POST", uri, nil, nil); err != nil { + return err + } + return nil +} + +func (client *DockerClient) Version() (*Version, error) { + uri := fmt.Sprintf("/%s/version", APIVersion) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + version := &Version{} + err = json.Unmarshal(data, version) + if err != nil { + return nil, err + } + return version, nil +} + +func (client *DockerClient) PushImage(name string, tag string, auth *AuthConfig) error { + v := url.Values{} + if tag != "" { + v.Set("tag", tag) + } + uri := fmt.Sprintf("/%s/images/%s/push?%s", APIVersion, url.QueryEscape(name), v.Encode()) + req, err := http.NewRequest("POST", client.URL.String()+uri, nil) + if auth != nil { + if encodedAuth, err := auth.encode(); err != nil { + return err + } else { + req.Header.Add("X-Registry-Auth", encodedAuth) + } + } + resp, err := client.HTTPClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + var finalObj map[string]interface{} + for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) { + } + if err != io.EOF { + return err + } + if err, ok := finalObj["error"]; ok { + return fmt.Errorf("%v", err) + } + return nil +} + +func (client *DockerClient) PullImage(name string, auth *AuthConfig) error { + v := url.Values{} + v.Set("fromImage", name) + uri := fmt.Sprintf("/%s/images/create?%s", APIVersion, v.Encode()) + req, err := http.NewRequest("POST", client.URL.String()+uri, nil) + if auth != nil { + encoded_auth, err := auth.encode() + if err != nil { + return err + } + req.Header.Add("X-Registry-Auth", encoded_auth) + } + resp, err := client.HTTPClient.Do(req) + if err != nil { + return err + } + + defer resp.Body.Close() + if resp.StatusCode == 404 { + return ErrNotFound + } + if resp.StatusCode >= 400 { + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + return fmt.Errorf("%s", string(data)) + } + + var finalObj map[string]interface{} + for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) { + } + if err != io.EOF { + return err + } + if err, ok := finalObj["error"]; ok { + return fmt.Errorf("%v", err) + } + return nil +} + +func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) { + uri := fmt.Sprintf("/%s/images/%s/json", APIVersion, id) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + info := &ImageInfo{} + err = json.Unmarshal(data, info) + if err != nil { + return nil, err + } + return info, nil +} + +func (client *DockerClient) LoadImage(reader io.Reader) error { + uri := fmt.Sprintf("/%s/images/load", APIVersion) + _, err := client.doStreamRequest("POST", uri, reader, nil) + return err +} + +func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error { + argForce := 0 + argVolumes := 0 + if force == true { + argForce = 1 + } + if volumes == true { + argVolumes = 1 + } + args := fmt.Sprintf("force=%d&v=%d", argForce, argVolumes) + uri := fmt.Sprintf("/%s/containers/%s?%s", APIVersion, id, args) + _, err := client.doRequest("DELETE", uri, nil, nil) + return err +} + +func (client *DockerClient) ListImages(all bool) ([]*Image, error) { + argAll := 0 + if all { + argAll = 1 + } + uri := fmt.Sprintf("/%s/images/json?all=%d", APIVersion, argAll) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + var images []*Image + if err := json.Unmarshal(data, &images); err != nil { + return nil, err + } + return images, nil +} + +func (client *DockerClient) RemoveImage(name string, force bool) ([]*ImageDelete, error) { + argForce := 0 + if force { + argForce = 1 + } + + args := fmt.Sprintf("force=%d", argForce) + uri := fmt.Sprintf("/%s/images/%s?%s", APIVersion, name, args) + data, err := client.doRequest("DELETE", uri, nil, nil) + if err != nil { + return nil, err + } + var imageDelete []*ImageDelete + if err := json.Unmarshal(data, &imageDelete); err != nil { + return nil, err + } + return imageDelete, nil +} + +func (client *DockerClient) SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error) { + term := query + if registry != "" { + term = registry + "/" + term + } + uri := fmt.Sprintf("/%s/images/search?term=%s", APIVersion, term) + headers := map[string]string{} + if auth != nil { + if encodedAuth, err := auth.encode(); err != nil { + return nil, err + } else { + headers["X-Registry-Auth"] = encodedAuth + } + } + data, err := client.doRequest("GET", uri, nil, headers) + if err != nil { + return nil, err + } + var imageSearches []ImageSearch + if err := json.Unmarshal(data, &imageSearches); err != nil { + return nil, err + } + return imageSearches, nil +} + +func (client *DockerClient) PauseContainer(id string) error { + uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id) + _, err := client.doRequest("POST", uri, nil, nil) + if err != nil { + return err + } + return nil +} +func (client *DockerClient) UnpauseContainer(id string) error { + uri := fmt.Sprintf("/%s/containers/%s/unpause", APIVersion, id) + _, err := client.doRequest("POST", uri, nil, nil) + if err != nil { + return err + } + return nil +} + +func (client *DockerClient) RenameContainer(oldName string, newName string) error { + uri := fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName) + _, err := client.doRequest("POST", uri, nil, nil) + return err +} + +func (client *DockerClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) { + var fromSrc string + v := &url.Values{} + if source == "" { + fromSrc = "-" + } else { + fromSrc = source + } + + v.Set("fromSrc", fromSrc) + v.Set("repo", repository) + if tag != "" { + v.Set("tag", tag) + } + + var in io.Reader + if fromSrc == "-" { + in = tar + } + return client.doStreamRequest("POST", "/images/create?"+v.Encode(), in, nil) +} + +func (client *DockerClient) BuildImage(image *BuildImage) (io.ReadCloser, error) { + v := url.Values{} + + if image.DockerfileName != "" { + v.Set("dockerfile", image.DockerfileName) + } + if image.RepoName != "" { + v.Set("t", image.RepoName) + } + if image.RemoteURL != "" { + v.Set("remote", image.RemoteURL) + } + if image.NoCache { + v.Set("nocache", "1") + } + if image.Pull { + v.Set("pull", "1") + } + if image.Remove { + v.Set("rm", "1") + } else { + v.Set("rm", "0") + } + if image.ForceRemove { + v.Set("forcerm", "1") + } + if image.SuppressOutput { + v.Set("q", "1") + } + + v.Set("memory", strconv.FormatInt(image.Memory, 10)) + v.Set("memswap", strconv.FormatInt(image.MemorySwap, 10)) + v.Set("cpushares", strconv.FormatInt(image.CpuShares, 10)) + v.Set("cpuperiod", strconv.FormatInt(image.CpuPeriod, 10)) + v.Set("cpuquota", strconv.FormatInt(image.CpuQuota, 10)) + v.Set("cpusetcpus", image.CpuSetCpus) + v.Set("cpusetmems", image.CpuSetMems) + v.Set("cgroupparent", image.CgroupParent) + if image.BuildArgs != nil { + buildArgsJSON, err := json.Marshal(image.BuildArgs) + if err != nil { + return nil, err + } + v.Set("buildargs", string(buildArgsJSON)) + } + + headers := make(map[string]string) + if image.Config != nil { + encoded_config, err := image.Config.encode() + if err != nil { + return nil, err + } + headers["X-Registry-Config"] = encoded_config + } + if image.Context != nil { + headers["Content-Type"] = "application/tar" + } + + uri := fmt.Sprintf("/%s/build?%s", APIVersion, v.Encode()) + return client.doStreamRequest("POST", uri, image.Context, headers) +} + +func (client *DockerClient) ListVolumes() ([]*Volume, error) { + uri := fmt.Sprintf("/%s/volumes", APIVersion) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + var volumesList VolumesListResponse + if err := json.Unmarshal(data, &volumesList); err != nil { + return nil, err + } + return volumesList.Volumes, nil +} + +func (client *DockerClient) RemoveVolume(name string) error { + uri := fmt.Sprintf("/%s/volumes/%s", APIVersion, name) + _, err := client.doRequest("DELETE", uri, nil, nil) + return err +} + +func (client *DockerClient) CreateVolume(request *VolumeCreateRequest) (*Volume, error) { + data, err := json.Marshal(request) + if err != nil { + return nil, err + } + uri := fmt.Sprintf("/%s/volumes/create", APIVersion) + data, err = client.doRequest("POST", uri, data, nil) + if err != nil { + return nil, err + } + volume := &Volume{} + err = json.Unmarshal(data, volume) + return volume, err +} + +func (client *DockerClient) ListNetworks(filters string) ([]*NetworkResource, error) { + uri := fmt.Sprintf("/%s/networks", APIVersion) + + if filters != "" { + uri += "&filters=" + filters + } + + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + ret := []*NetworkResource{} + err = json.Unmarshal(data, &ret) + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *DockerClient) InspectNetwork(id string) (*NetworkResource, error) { + uri := fmt.Sprintf("/%s/networks/%s", APIVersion, id) + + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + ret := &NetworkResource{} + err = json.Unmarshal(data, ret) + if err != nil { + return nil, err + } + + return ret, nil +} + +func (client *DockerClient) CreateNetwork(config *NetworkCreate) (*NetworkCreateResponse, error) { + data, err := json.Marshal(config) + if err != nil { + return nil, err + } + uri := fmt.Sprintf("/%s/networks/create", APIVersion) + data, err = client.doRequest("POST", uri, data, nil) + if err != nil { + return nil, err + } + ret := &NetworkCreateResponse{} + err = json.Unmarshal(data, ret) + return ret, nil +} + +func (client *DockerClient) ConnectNetwork(id, container string) error { + data, err := json.Marshal(NetworkConnect{Container: container}) + if err != nil { + return err + } + uri := fmt.Sprintf("/%s/networks/%s/connect", APIVersion, id) + _, err = client.doRequest("POST", uri, data, nil) + return err +} + +func (client *DockerClient) DisconnectNetwork(id, container string, force bool) error { + data, err := json.Marshal(NetworkDisconnect{Container: container, Force: force}) + if err != nil { + return err + } + uri := fmt.Sprintf("/%s/networks/%s/disconnect", APIVersion, id) + _, err = client.doRequest("POST", uri, data, nil) + return err +} + +func (client *DockerClient) RemoveNetwork(id string) error { + uri := fmt.Sprintf("/%s/networks/%s", APIVersion, id) + _, err := client.doRequest("DELETE", uri, nil, nil) + return err +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/dockerclient_test.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/dockerclient_test.go new file mode 100644 index 00000000..7c7b3f59 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/dockerclient_test.go @@ -0,0 +1,320 @@ +package dockerclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "reflect" + "strings" + "testing" + "time" + + "github.com/docker/docker/pkg/stdcopy" +) + +func assertEqual(t *testing.T, a interface{}, b interface{}, message string) { + if a == b { + return + } + if len(message) == 0 { + message = fmt.Sprintf("%v != %v", a, b) + } + t.Fatal(message) +} + +func testDockerClient(t *testing.T) *DockerClient { + client, err := NewDockerClient(testHTTPServer.URL, nil) + if err != nil { + t.Fatal("Cannot init the docker client") + } + return client +} + +func ExampleDockerClient_AttachContainer() { + docker, err := NewDockerClient("unix:///var/run/docker.sock", nil) + if err != nil { + panic(err) + } + cID, err := docker.CreateContainer(&ContainerConfig{ + Cmd: []string{"echo", "hi"}, + Image: "busybox", + }, "", nil) + if err != nil { + panic(err) + } + done := make(chan struct{}) + if body, err := docker.AttachContainer(cID, &AttachOptions{ + Stream: true, + Stdout: true, + }); err != nil { + panic(err) + } else { + go func() { + defer body.Close() + if _, err := stdcopy.StdCopy(os.Stdout, os.Stderr, body); err != nil { + panic(err) + } + close(done) + }() + } + + if err := docker.StartContainer(cID, nil); err != nil { + panic(err) + } + <-done +} + +func TestInfo(t *testing.T) { + client := testDockerClient(t) + info, err := client.Info() + if err != nil { + t.Fatal("Cannot get server info") + } + assertEqual(t, info.Images, int64(1), "") + assertEqual(t, info.Containers, int64(2), "") +} + +func TestKillContainer(t *testing.T) { + client := testDockerClient(t) + if err := client.KillContainer("23132acf2ac", "5"); err != nil { + t.Fatal("cannot kill container: %s", err) + } +} + +func TestWait(t *testing.T) { + client := testDockerClient(t) + + // This provokes an error on the server. + select { + case wr := <-client.Wait("1234"): + assertEqual(t, wr.ExitCode, int(-1), "") + case <-time.After(2 * time.Second): + t.Fatal("Timed out!") + } + + // Valid case. + select { + case wr := <-client.Wait("valid-id"): + assertEqual(t, wr.ExitCode, int(0), "") + case <-time.After(2 * time.Second): + t.Fatal("Timed out!") + } +} + +func TestPullImage(t *testing.T) { + client := testDockerClient(t) + err := client.PullImage("busybox", nil) + if err != nil { + t.Fatal("unable to pull busybox") + } + + err = client.PullImage("haproxy", nil) + if err != nil { + t.Fatal("unable to pull haproxy") + } + + err = client.PullImage("wrongimg", nil) + if err == nil { + t.Fatal("should return error when it fails to pull wrongimg") + } +} + +func TestListContainers(t *testing.T) { + client := testDockerClient(t) + containers, err := client.ListContainers(true, false, "") + if err != nil { + t.Fatal("cannot get containers: %s", err) + } + assertEqual(t, len(containers), 1, "") + cnt := containers[0] + assertEqual(t, cnt.SizeRw, int64(0), "") +} + +func TestContainerChanges(t *testing.T) { + client := testDockerClient(t) + changes, err := client.ContainerChanges("foobar") + if err != nil { + t.Fatal("cannot get container changes: %s", err) + } + assertEqual(t, len(changes), 3, "unexpected number of changes") + c := changes[0] + assertEqual(t, c.Path, "/dev", "unexpected") + assertEqual(t, c.Kind, 0, "unexpected") +} + +func TestListContainersWithSize(t *testing.T) { + client := testDockerClient(t) + containers, err := client.ListContainers(true, true, "") + if err != nil { + t.Fatal("cannot get containers: %s", err) + } + assertEqual(t, len(containers), 1, "") + cnt := containers[0] + assertEqual(t, cnt.SizeRw, int64(123), "") +} + +func TestListContainersWithFilters(t *testing.T) { + client := testDockerClient(t) + containers, err := client.ListContainers(true, true, "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce']}") + if err != nil { + t.Fatal("cannot get containers: %s", err) + } + assertEqual(t, len(containers), 1, "") + + containers, err = client.ListContainers(true, true, "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688cf']}") + if err != nil { + t.Fatal("cannot get containers: %s", err) + } + assertEqual(t, len(containers), 0, "") +} + +func TestContainerLogs(t *testing.T) { + client := testDockerClient(t) + containerId := "foobar" + logOptions := &LogOptions{ + Follow: true, + Stdout: true, + Stderr: true, + Timestamps: true, + Tail: 10, + } + logsReader, err := client.ContainerLogs(containerId, logOptions) + if err != nil { + t.Fatal("cannot read logs from server") + } + + stdoutBuffer := new(bytes.Buffer) + stderrBuffer := new(bytes.Buffer) + if _, err = stdcopy.StdCopy(stdoutBuffer, stderrBuffer, logsReader); err != nil { + t.Fatal("cannot read logs from logs reader") + } + stdoutLogs := strings.TrimSpace(stdoutBuffer.String()) + stderrLogs := strings.TrimSpace(stderrBuffer.String()) + stdoutLogLines := strings.Split(stdoutLogs, "\n") + stderrLogLines := strings.Split(stderrLogs, "\n") + if len(stdoutLogLines) != 5 { + t.Fatalf("wrong number of stdout logs: len=%d", len(stdoutLogLines)) + } + if len(stderrLogLines) != 5 { + t.Fatalf("wrong number of stderr logs: len=%d", len(stdoutLogLines)) + } + for i, line := range stdoutLogLines { + expectedSuffix := fmt.Sprintf("Z line %d", 41+2*i) + if !strings.HasSuffix(line, expectedSuffix) { + t.Fatalf("expected stdout log line \"%s\" to end with \"%s\"", line, expectedSuffix) + } + } + for i, line := range stderrLogLines { + expectedSuffix := fmt.Sprintf("Z line %d", 40+2*i) + if !strings.HasSuffix(line, expectedSuffix) { + t.Fatalf("expected stderr log line \"%s\" to end with \"%s\"", line, expectedSuffix) + } + } +} + +func TestContainerStats(t *testing.T) { + client := testDockerClient(t) + var expectedContainerStats Stats + if err := json.Unmarshal([]byte(statsResp), &expectedContainerStats); err != nil { + t.Fatalf("cannot parse expected resp: %s", err.Error()) + } + containerIds := []string{"foobar", "foo"} + expectedResults := [][]StatsOrError{ + {{Stats: expectedContainerStats}, {Error: fmt.Errorf("invalid character 'i' looking for beginning of value")}}, + {{Stats: expectedContainerStats}, {Stats: expectedContainerStats}}, + } + + for i := range containerIds { + t.Logf("on outer iter %d\n", i) + stopChan := make(chan struct{}) + statsOrErrorChan, err := client.ContainerStats(containerIds[i], stopChan) + if err != nil { + t.Fatalf("cannot get stats from server: %s", err.Error()) + } + + for j, expectedResult := range expectedResults[i] { + t.Logf("on iter %d\n", j) + containerStatsOrError := <-statsOrErrorChan + if containerStatsOrError.Error != nil { + if expectedResult.Error == nil { + t.Fatalf("index %d, got unexpected error %v", j, containerStatsOrError.Error) + } else if containerStatsOrError.Error.Error() == expectedResult.Error.Error() { + // continue so that we don't try to + // compare error values directly + continue + } else { + t.Fatalf("index %d, expected error %q but got %q", j, expectedResult.Error, containerStatsOrError.Error) + } + } + if !reflect.DeepEqual(containerStatsOrError, expectedResult) { + t.Fatalf("index %d, got:\n%#v\nexpected:\n%#v", j, containerStatsOrError, expectedResult) + } + t.Logf("done with iter %d\n", j) + } + close(stopChan) + t.Logf("done with outer iter %d\n", i) + } +} + +func TestMonitorEvents(t *testing.T) { + client := testDockerClient(t) + decoder := json.NewDecoder(bytes.NewBufferString(eventsResp)) + var expectedEvents []Event + for { + var event Event + if err := decoder.Decode(&event); err != nil { + if err == io.EOF { + break + } else { + t.Fatalf("cannot parse expected resp: %s", err.Error()) + } + } else { + expectedEvents = append(expectedEvents, event) + } + } + + // test passing stop chan + stopChan := make(chan struct{}) + eventInfoChan, err := client.MonitorEvents(nil, stopChan) + if err != nil { + t.Fatalf("cannot get events from server: %s", err.Error()) + } + + eventInfo := <-eventInfoChan + if eventInfo.Error != nil || eventInfo.Event != expectedEvents[0] { + t.Fatalf("got:\n%#v\nexpected:\n%#v", eventInfo, expectedEvents[0]) + } + close(stopChan) + for i := 0; i < 3; i++ { + _, ok := <-eventInfoChan + if i == 2 && ok { + t.Fatalf("read more than 2 events successfully after closing stopChan") + } + } + + // test when you don't pass stop chan + eventInfoChan, err = client.MonitorEvents(nil, nil) + if err != nil { + t.Fatalf("cannot get events from server: %s", err.Error()) + } + + for i, expectedEvent := range expectedEvents { + t.Logf("on iter %d\n", i) + eventInfo := <-eventInfoChan + if eventInfo.Error != nil || eventInfo.Event != expectedEvent { + t.Fatalf("index %d, got:\n%#v\nexpected:\n%#v", i, eventInfo, expectedEvent) + } + t.Logf("done with iter %d\n", i) + } +} + +func TestDockerClientInterface(t *testing.T) { + iface := reflect.TypeOf((*Client)(nil)).Elem() + test := testDockerClient(t) + + if !reflect.TypeOf(test).Implements(iface) { + t.Fatalf("DockerClient does not implement the Client interface") + } +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/engine_mock_test.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/engine_mock_test.go new file mode 100644 index 00000000..b2382a15 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/engine_mock_test.go @@ -0,0 +1,254 @@ +package dockerclient + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/http/httptest" + "strconv" + "time" + + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/jsonlog" + "github.com/docker/docker/pkg/stdcopy" + "github.com/gorilla/mux" +) + +var ( + testHTTPServer *httptest.Server +) + +func init() { + r := mux.NewRouter() + baseURL := "/" + APIVersion + r.HandleFunc(baseURL+"/info", handlerGetInfo).Methods("GET") + r.HandleFunc(baseURL+"/containers/json", handlerGetContainers).Methods("GET") + r.HandleFunc(baseURL+"/containers/{id}/logs", handleContainerLogs).Methods("GET") + r.HandleFunc(baseURL+"/containers/{id}/changes", handleContainerChanges).Methods("GET") + r.HandleFunc(baseURL+"/containers/{id}/stats", handleContainerStats).Methods("GET") + r.HandleFunc(baseURL+"/containers/{id}/kill", handleContainerKill).Methods("POST") + r.HandleFunc(baseURL+"/containers/{id}/wait", handleWait).Methods("POST") + r.HandleFunc(baseURL+"/images/create", handleImagePull).Methods("POST") + r.HandleFunc(baseURL+"/events", handleEvents).Methods("GET") + testHTTPServer = httptest.NewServer(handlerAccessLog(r)) +} + +func handlerAccessLog(handler http.Handler) http.Handler { + logHandler := func(w http.ResponseWriter, r *http.Request) { + log.Printf("%s \"%s %s\"", r.RemoteAddr, r.Method, r.URL) + handler.ServeHTTP(w, r) + } + return http.HandlerFunc(logHandler) +} + +func handleContainerKill(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "{%q:%q", "Id", "421373210afd132") +} + +func handleWait(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + if vars["id"] == "valid-id" { + fmt.Fprintf(w, `{"StatusCode":0}`) + } else { + http.Error(w, "failed", 500) + } +} + +func handleImagePull(w http.ResponseWriter, r *http.Request) { + imageName := r.URL.Query()["fromImage"][0] + responses := []map[string]interface{}{{ + "status": fmt.Sprintf("Pulling repository mydockerregistry/%s", imageName), + }} + switch imageName { + case "busybox": + responses = append(responses, map[string]interface{}{ + "status": "Status: Image is up to date for mydockerregistry/busybox", + }) + case "haproxy": + fmt.Fprintf(w, haproxyPullOutput) + return + default: + errorMsg := fmt.Sprintf("Error: image %s not found", imageName) + responses = append(responses, map[string]interface{}{ + "errorDetail": map[string]interface{}{ + "message": errorMsg, + }, + "error": errorMsg, + }) + } + for _, response := range responses { + json.NewEncoder(w).Encode(response) + } +} + +func handleContainerLogs(w http.ResponseWriter, r *http.Request) { + var outStream, errStream io.Writer + outStream = ioutils.NewWriteFlusher(w) + + // not sure how to test follow + if err := r.ParseForm(); err != nil { + http.Error(w, err.Error(), 500) + } + stdout, stderr := getBoolValue(r.Form.Get("stdout")), getBoolValue(r.Form.Get("stderr")) + if stderr { + errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) + } + if stdout { + outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) + } + var i int + if tail, err := strconv.Atoi(r.Form.Get("tail")); err == nil && tail > 0 { + i = 50 - tail + if i < 0 { + i = 0 + } + } + for ; i < 50; i++ { + line := fmt.Sprintf("line %d", i) + if getBoolValue(r.Form.Get("timestamps")) { + l := &jsonlog.JSONLog{Log: line, Created: time.Now().UTC()} + line = fmt.Sprintf("%s %s", l.Created.Format(jsonlog.RFC3339NanoFixed), line) + } + if i%2 == 0 && stderr { + fmt.Fprintln(errStream, line) + } else if i%2 == 1 && stdout { + fmt.Fprintln(outStream, line) + } + } +} + +func handleContainerChanges(w http.ResponseWriter, r *http.Request) { + writeHeaders(w, 200, "changes") + body := `[ + { + "Path": "/dev", + "Kind": 0 + }, + { + "Path": "/dev/kmsg", + "Kind": 1 + }, + { + "Path": "/test", + "Kind": 1 + } + ]` + w.Write([]byte(body)) +} + +func handleContainerStats(w http.ResponseWriter, r *http.Request) { + switch mux.Vars(r)["id"] { + case "foobar": + fmt.Fprintf(w, "%s invalidresp", statsResp) + default: + fmt.Fprintf(w, "%s %s", statsResp, statsResp) + } +} + +func getBoolValue(boolString string) bool { + switch boolString { + case "1": + return true + case "True": + return true + case "true": + return true + default: + return false + } +} + +func writeHeaders(w http.ResponseWriter, code int, jobName string) { + h := w.Header() + h.Add("Content-Type", "application/json") + if jobName != "" { + h.Add("Job-Name", jobName) + } + w.WriteHeader(code) +} + +func handlerGetInfo(w http.ResponseWriter, r *http.Request) { + writeHeaders(w, 200, "info") + body := `{ + "Containers": 2, + "Debug": 1, + "Driver": "aufs", + "DriverStatus": [["Root Dir", "/mnt/sda1/var/lib/docker/aufs"], + ["Dirs", "0"]], + "ExecutionDriver": "native-0.2", + "IPv4Forwarding": 1, + "Images": 1, + "IndexServerAddress": "https://index.docker.io/v1/", + "InitPath": "/usr/local/bin/docker", + "InitSha1": "", + "KernelVersion": "3.16.4-tinycore64", + "MemoryLimit": 1, + "NEventsListener": 0, + "NFd": 10, + "NGoroutines": 11, + "OperatingSystem": "Boot2Docker 1.3.1 (TCL 5.4); master : a083df4 - Thu Jan 01 00:00:00 UTC 1970", + "SwapLimit": 1}` + w.Write([]byte(body)) +} + +func handlerGetContainers(w http.ResponseWriter, r *http.Request) { + writeHeaders(w, 200, "containers") + body := `[ + { + "Status": "Up 39 seconds", + "Ports": [ + { + "Type": "tcp", + "PublicPort": 49163, + "PrivatePort": 8080, + "IP": "0.0.0.0" + } + ], + "Names": [ + "/trusting_heisenberg" + ], + "Image": "foo:latest", + "Id": "332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce", + "Created": 1415720105, + "Command": "/bin/go-run" + } + ]` + if v, ok := r.URL.Query()["size"]; ok { + if v[0] == "1" { + body = `[ + { + "Status": "Up 39 seconds", + "Ports": [ + { + "Type": "tcp", + "PublicPort": 49163, + "PrivatePort": 8080, + "IP": "0.0.0.0" + } + ], + "Names": [ + "/trusting_heisenberg" + ], + "Image": "foo:latest", + "Id": "332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce", + "Created": 1415720105, + "SizeRootFs": 12345, + "SizeRW": 123, + "Command": "/bin/go-run" + } + ]` + } + } + if v, ok := r.URL.Query()["filters"]; ok { + if v[0] != "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce']}" { + body = "[]" + } + } + w.Write([]byte(body)) +} + +func handleEvents(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(eventsResp)) +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/example_responses.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/example_responses.go new file mode 100644 index 00000000..e4c890dc --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/example_responses.go @@ -0,0 +1,15 @@ +package dockerclient + +var haproxyPullOutput = `{"status":"The image you are pulling has been verified","id":"haproxy:1"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"The image you are pulling has been verified","id":"haproxy:1.4"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"63a1b9929e14"}{"status":"Already exists","progressDetail":{},"id":"af43bf7d176e"}{"status":"Already exists","progressDetail":{},"id":"851aac2d69aa"}{"status":"Already exists","progressDetail":{},"id":"345053a92c95"}{"status":"Already exists","progressDetail":{},"id":"b41231d429c9"}{"status":"The image you are pulling has been verified","id":"haproxy:1.4.25"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"63a1b9929e14"}{"status":"Already exists","progressDetail":{},"id":"af43bf7d176e"}{"status":"Already exists","progressDetail":{},"id":"851aac2d69aa"}{"status":"Already exists","progressDetail":{},"id":"345053a92c95"}{"status":"Already exists","progressDetail":{},"id":"b41231d429c9"}{"status":"The image you are pulling has been verified","id":"haproxy:1.5"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"The image you are pulling has been verified","id":"haproxy:1.5.10"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"The image you are pulling has been verified","id":"haproxy:1.5.9"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"3d894e6f7e63"}{"status":"Already exists","progressDetail":{},"id":"4d949c40bc77"}{"status":"Already exists","progressDetail":{},"id":"55e031889365"}{"status":"Already exists","progressDetail":{},"id":"c7aa675e1876"}{"status":"The image you are pulling has been verified","id":"haproxy:latest"} +{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"Status: Image is up to date for haproxy"} +` + +var statsResp = `{"read":"2015-02-02T17:06:08.187833376-05:00","network":{"rx_bytes":99988,"rx_packets":928,"rx_errors":0,"rx_dropped":0,"tx_bytes":1786548,"tx_packets":877,"tx_errors":0,"tx_dropped":0},"cpu_stats":{"cpu_usage":{"total_usage":170018598,"percpu_usage":[170018598],"usage_in_kernelmode":30000000,"usage_in_usermode":70000000},"system_cpu_usage":9020930000000,"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"memory_stats":{"usage":18022400,"max_usage":20541440,"stats":{"active_anon":6213632,"active_file":176128,"cache":11808768,"hierarchical_memory_limit":9223372036854775807,"hierarchical_memsw_limit":9223372036854775807,"inactive_anon":0,"inactive_file":11632640,"mapped_file":5165056,"pgfault":2535,"pgmajfault":13,"pgpgin":4293,"pgpgout":1937,"rss":6213632,"rss_huge":2097152,"swap":0,"total_active_anon":6213632,"total_active_file":176128,"total_cache":11808768,"total_inactive_anon":0,"total_inactive_file":11632640,"total_mapped_file":5165056,"total_pgfault":2535,"total_pgmajfault":13,"total_pgpgin":4293,"total_pgpgout":1937,"total_rss":6213632,"total_rss_huge":2097152,"total_swap":0,"total_unevictable":0,"unevictable":0},"failcnt":0,"limit":1041051648},"blkio_stats":{"io_service_bytes_recursive":[{"major":7,"minor":0,"op":"Read","value":28672},{"major":7,"minor":0,"op":"Write","value":0},{"major":7,"minor":0,"op":"Sync","value":0},{"major":7,"minor":0,"op":"Async","value":28672},{"major":7,"minor":0,"op":"Total","value":28672},{"major":253,"minor":0,"op":"Read","value":28672},{"major":253,"minor":0,"op":"Write","value":0},{"major":253,"minor":0,"op":"Sync","value":0},{"major":253,"minor":0,"op":"Async","value":28672},{"major":253,"minor":0,"op":"Total","value":28672},{"major":253,"minor":7,"op":"Read","value":11718656},{"major":253,"minor":7,"op":"Write","value":0},{"major":253,"minor":7,"op":"Sync","value":0},{"major":253,"minor":7,"op":"Async","value":11718656},{"major":253,"minor":7,"op":"Total","value":11718656},{"major":202,"minor":0,"op":"Read","value":0},{"major":202,"minor":0,"op":"Write","value":0},{"major":202,"minor":0,"op":"Sync","value":0},{"major":202,"minor":0,"op":"Async","value":0},{"major":202,"minor":0,"op":"Total","value":0}],"io_serviced_recursive":[{"major":7,"minor":0,"op":"Read","value":7},{"major":7,"minor":0,"op":"Write","value":0},{"major":7,"minor":0,"op":"Sync","value":0},{"major":7,"minor":0,"op":"Async","value":7},{"major":7,"minor":0,"op":"Total","value":7},{"major":253,"minor":0,"op":"Read","value":7},{"major":253,"minor":0,"op":"Write","value":0},{"major":253,"minor":0,"op":"Sync","value":0},{"major":253,"minor":0,"op":"Async","value":7},{"major":253,"minor":0,"op":"Total","value":7},{"major":253,"minor":7,"op":"Read","value":312},{"major":253,"minor":7,"op":"Write","value":0},{"major":253,"minor":7,"op":"Sync","value":0},{"major":253,"minor":7,"op":"Async","value":312},{"major":253,"minor":7,"op":"Total","value":312},{"major":202,"minor":0,"op":"Read","value":0},{"major":202,"minor":0,"op":"Write","value":0},{"major":202,"minor":0,"op":"Sync","value":0},{"major":202,"minor":0,"op":"Async","value":0},{"major":202,"minor":0,"op":"Total","value":0}],"io_queue_recursive":[],"io_service_time_recursive":[],"io_wait_time_recursive":[],"io_merged_recursive":[],"io_time_recursive":[],"sectors_recursive":[]}}` + +var eventsResp = `{"status":"pull","id":"nginx:latest","time":1428620433}{"status":"create","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"start","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"die","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620442}{"status":"create","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"start","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"die","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"pull","id":"debian:latest","time":1428620453}{"status":"create","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"start","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"die","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"create","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"start","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"pause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620462}{"status":"unpause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620466}{"status":"die","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620469}` diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/examples/events.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/examples/events.go new file mode 100644 index 00000000..2d6de40c --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/examples/events.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/samalba/dockerclient" + "log" + "os" + "os/signal" + "syscall" +) + +func eventCallback(e *dockerclient.Event, ec chan error, args ...interface{}) { + log.Println(e) +} + +var ( + client *dockerclient.DockerClient +) + +func waitForInterrupt() { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) + for _ = range sigChan { + client.StopAllMonitorEvents() + os.Exit(0) + } +} + +func main() { + docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil) + if err != nil { + log.Fatal(err) + } + + client = docker + + client.StartMonitorEvents(eventCallback, nil) + + waitForInterrupt() +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/examples/stats/stats.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/examples/stats/stats.go new file mode 100644 index 00000000..81ee2fb4 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/examples/stats/stats.go @@ -0,0 +1,43 @@ +package main + +import ( + "github.com/samalba/dockerclient" + "log" + "os" + "os/signal" + "syscall" +) + +func statCallback(id string, stat *dockerclient.Stats, ec chan error, args ...interface{}) { + log.Println(stat) +} + +func waitForInterrupt() { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) + for _ = range sigChan { + os.Exit(0) + } +} + +func main() { + docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil) + if err != nil { + log.Fatal(err) + } + + containerConfig := &dockerclient.ContainerConfig{Image: "busybox", Cmd: []string{"sh"}} + containerId, err := docker.CreateContainer(containerConfig, "", nil) + if err != nil { + log.Fatal(err) + } + + // Start the container + err = docker.StartContainer(containerId, nil) + if err != nil { + log.Fatal(err) + } + docker.StartMonitorStats(containerId, statCallback, nil) + + waitForInterrupt() +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/interface.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/interface.go new file mode 100644 index 00000000..b373055e --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/interface.go @@ -0,0 +1,66 @@ +package dockerclient + +import ( + "io" +) + +type Callback func(*Event, chan error, ...interface{}) + +type StatCallback func(string, *Stats, chan error, ...interface{}) + +type Client interface { + Info() (*Info, error) + ListContainers(all, size bool, filters string) ([]Container, error) + InspectContainer(id string) (*ContainerInfo, error) + InspectImage(id string) (*ImageInfo, error) + CreateContainer(config *ContainerConfig, name string, authConfig *AuthConfig) (string, error) + ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) + ContainerChanges(id string) ([]*ContainerChanges, error) + // ContainerStats takes a container ID and an optional stop channel and + // returns a StatsOrError channel. If an error is ever sent, then no + // more stats will be sent on that channel. If a stop channel is + // provided, events will stop being monitored after the stop channel is + // closed. + ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error) + ExecCreate(config *ExecConfig) (string, error) + ExecStart(id string, config *ExecConfig) error + ExecResize(id string, width, height int) error + StartContainer(id string, config *HostConfig) error + AttachContainer(id string, options *AttachOptions) (io.ReadCloser, error) + StopContainer(id string, timeout int) error + RestartContainer(id string, timeout int) error + KillContainer(id, signal string) error + Wait(id string) <-chan WaitResult + // MonitorEvents takes options and an optional stop channel, and returns + // an EventOrError channel. If an error is ever sent, then no more + // events will be sent. If a stop channel is provided, events will stop + // being monitored after the stop channel is closed. + MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) + StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) + StopAllMonitorEvents() + StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) + StopAllMonitorStats() + TagImage(nameOrID string, repo string, tag string, force bool) error + Version() (*Version, error) + PullImage(name string, auth *AuthConfig) error + PushImage(name string, tag string, auth *AuthConfig) error + LoadImage(reader io.Reader) error + RemoveContainer(id string, force, volumes bool) error + ListImages(all bool) ([]*Image, error) + RemoveImage(name string, force bool) ([]*ImageDelete, error) + SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error) + PauseContainer(name string) error + UnpauseContainer(name string) error + RenameContainer(oldName string, newName string) error + ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) + BuildImage(image *BuildImage) (io.ReadCloser, error) + ListVolumes() ([]*Volume, error) + RemoveVolume(name string) error + CreateVolume(request *VolumeCreateRequest) (*Volume, error) + ListNetworks(filters string) ([]*NetworkResource, error) + InspectNetwork(id string) (*NetworkResource, error) + CreateNetwork(config *NetworkCreate) (*NetworkCreateResponse, error) + ConnectNetwork(id, container string) error + DisconnectNetwork(id, container string, force bool) error + RemoveNetwork(id string) error +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/mockclient/mock.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/mockclient/mock.go new file mode 100644 index 00000000..9da36b87 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/mockclient/mock.go @@ -0,0 +1,237 @@ +package mockclient + +import ( + "io" + + "github.com/samalba/dockerclient" + "github.com/stretchr/testify/mock" +) + +type MockClient struct { + mock.Mock +} + +func NewMockClient() *MockClient { + return &MockClient{} +} + +func (client *MockClient) Info() (*dockerclient.Info, error) { + args := client.Mock.Called() + return args.Get(0).(*dockerclient.Info), args.Error(1) +} + +func (client *MockClient) ListContainers(all bool, size bool, filters string) ([]dockerclient.Container, error) { + args := client.Mock.Called(all, size, filters) + return args.Get(0).([]dockerclient.Container), args.Error(1) +} + +func (client *MockClient) InspectContainer(id string) (*dockerclient.ContainerInfo, error) { + args := client.Mock.Called(id) + return args.Get(0).(*dockerclient.ContainerInfo), args.Error(1) +} + +func (client *MockClient) InspectImage(id string) (*dockerclient.ImageInfo, error) { + args := client.Mock.Called(id) + return args.Get(0).(*dockerclient.ImageInfo), args.Error(1) +} + +func (client *MockClient) CreateContainer(config *dockerclient.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (string, error) { + args := client.Mock.Called(config, name, authConfig) + return args.String(0), args.Error(1) +} + +func (client *MockClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) { + args := client.Mock.Called(id, options) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + +func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) { + args := client.Mock.Called(id) + return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1) +} + +func (client *MockClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan dockerclient.StatsOrError, error) { + args := client.Mock.Called(id, stopChan) + return args.Get(0).(<-chan dockerclient.StatsOrError), args.Error(1) +} + +func (client *MockClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) { + args := client.Mock.Called(id, options) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + +func (client *MockClient) StartContainer(id string, config *dockerclient.HostConfig) error { + args := client.Mock.Called(id, config) + return args.Error(0) +} + +func (client *MockClient) StopContainer(id string, timeout int) error { + args := client.Mock.Called(id, timeout) + return args.Error(0) +} + +func (client *MockClient) RestartContainer(id string, timeout int) error { + args := client.Mock.Called(id, timeout) + return args.Error(0) +} + +func (client *MockClient) KillContainer(id, signal string) error { + args := client.Mock.Called(id, signal) + return args.Error(0) +} + +func (client *MockClient) Wait(id string) <-chan dockerclient.WaitResult { + args := client.Mock.Called(id) + return args.Get(0).(<-chan dockerclient.WaitResult) +} + +func (client *MockClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) { + args := client.Mock.Called(options, stopChan) + return args.Get(0).(<-chan dockerclient.EventOrError), args.Error(1) +} + +func (client *MockClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) { + client.Mock.Called(cb, ec, args) +} + +func (client *MockClient) StopAllMonitorEvents() { + client.Mock.Called() +} + +func (client *MockClient) TagImage(nameOrID string, repo string, tag string, force bool) error { + args := client.Mock.Called(nameOrID, repo, tag, force) + return args.Error(0) +} + +func (client *MockClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) { + client.Mock.Called(id, cb, ec, args) +} + +func (client *MockClient) StopAllMonitorStats() { + client.Mock.Called() +} + +func (client *MockClient) Version() (*dockerclient.Version, error) { + args := client.Mock.Called() + return args.Get(0).(*dockerclient.Version), args.Error(1) +} + +func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig) error { + args := client.Mock.Called(name, auth) + return args.Error(0) +} + +func (client *MockClient) PushImage(name string, tag string, auth *dockerclient.AuthConfig) error { + args := client.Mock.Called(name, tag, auth) + return args.Error(0) +} + +func (client *MockClient) LoadImage(reader io.Reader) error { + args := client.Mock.Called(reader) + return args.Error(0) +} + +func (client *MockClient) RemoveContainer(id string, force, volumes bool) error { + args := client.Mock.Called(id, force, volumes) + return args.Error(0) +} + +func (client *MockClient) ListImages(all bool) ([]*dockerclient.Image, error) { + args := client.Mock.Called(all) + return args.Get(0).([]*dockerclient.Image), args.Error(1) +} + +func (client *MockClient) RemoveImage(name string, force bool) ([]*dockerclient.ImageDelete, error) { + args := client.Mock.Called(name, force) + return args.Get(0).([]*dockerclient.ImageDelete), args.Error(1) +} + +func (client *MockClient) SearchImages(query, registry string, authConfig *dockerclient.AuthConfig) ([]dockerclient.ImageSearch, error) { + args := client.Mock.Called(query, registry, authConfig) + return args.Get(0).([]dockerclient.ImageSearch), args.Error(1) +} + +func (client *MockClient) PauseContainer(name string) error { + args := client.Mock.Called(name) + return args.Error(0) +} + +func (client *MockClient) UnpauseContainer(name string) error { + args := client.Mock.Called(name) + return args.Error(0) +} + +func (client *MockClient) ExecCreate(config *dockerclient.ExecConfig) (string, error) { + args := client.Mock.Called(config) + return args.String(0), args.Error(1) +} + +func (client *MockClient) ExecStart(id string, config *dockerclient.ExecConfig) error { + args := client.Mock.Called(id, config) + return args.Error(0) +} + +func (client *MockClient) ExecResize(id string, width, height int) error { + args := client.Mock.Called(id, width, height) + return args.Error(0) +} + +func (client *MockClient) RenameContainer(oldName string, newName string) error { + args := client.Mock.Called(oldName, newName) + return args.Error(0) +} + +func (client *MockClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) { + args := client.Mock.Called(source, repository, tag, tar) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + +func (client *MockClient) BuildImage(image *dockerclient.BuildImage) (io.ReadCloser, error) { + args := client.Mock.Called(image) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + +func (client *MockClient) ListVolumes() ([]*dockerclient.Volume, error) { + args := client.Mock.Called() + return args.Get(0).([]*dockerclient.Volume), args.Error(1) +} + +func (client *MockClient) RemoveVolume(name string) error { + args := client.Mock.Called(name) + return args.Error(0) +} + +func (client *MockClient) CreateVolume(request *dockerclient.VolumeCreateRequest) (*dockerclient.Volume, error) { + args := client.Mock.Called(request) + return args.Get(0).(*dockerclient.Volume), args.Error(1) +} + +func (client *MockClient) ListNetworks(filters string) ([]*dockerclient.NetworkResource, error) { + args := client.Mock.Called(filters) + return args.Get(0).([]*dockerclient.NetworkResource), args.Error(1) +} + +func (client *MockClient) InspectNetwork(id string) (*dockerclient.NetworkResource, error) { + args := client.Mock.Called(id) + return args.Get(0).(*dockerclient.NetworkResource), args.Error(1) +} + +func (client *MockClient) CreateNetwork(config *dockerclient.NetworkCreate) (*dockerclient.NetworkCreateResponse, error) { + args := client.Mock.Called(config) + return args.Get(0).(*dockerclient.NetworkCreateResponse), args.Error(1) +} + +func (client *MockClient) ConnectNetwork(id, container string) error { + args := client.Mock.Called(id, container) + return args.Error(0) +} + +func (client *MockClient) DisconnectNetwork(id, container string, force bool) error { + args := client.Mock.Called(id, container, force) + return args.Error(0) +} + +func (client *MockClient) RemoveNetwork(id string) error { + args := client.Mock.Called(id) + return args.Error(0) +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/mockclient/mock_test.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/mockclient/mock_test.go new file mode 100644 index 00000000..8d91bcf6 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/mockclient/mock_test.go @@ -0,0 +1,32 @@ +package mockclient + +import ( + "reflect" + "testing" + + "github.com/samalba/dockerclient" +) + +func TestMock(t *testing.T) { + mock := NewMockClient() + mock.On("Version").Return(&dockerclient.Version{Version: "foo"}, nil).Once() + + v, err := mock.Version() + if err != nil { + t.Fatal(err) + } + if v.Version != "foo" { + t.Fatal(v) + } + + mock.Mock.AssertExpectations(t) +} + +func TestMockInterface(t *testing.T) { + iface := reflect.TypeOf((*dockerclient.Client)(nil)).Elem() + mock := NewMockClient() + + if !reflect.TypeOf(mock).Implements(iface) { + t.Fatalf("Mock does not implement the Client interface") + } +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/nopclient/nop.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/nopclient/nop.go new file mode 100644 index 00000000..659b57b7 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/nopclient/nop.go @@ -0,0 +1,199 @@ +package nopclient + +import ( + "errors" + "io" + + "github.com/samalba/dockerclient" +) + +var ( + ErrNoEngine = errors.New("Engine no longer exists") +) + +type NopClient struct { +} + +func NewNopClient() *NopClient { + return &NopClient{} +} + +func (client *NopClient) Info() (*dockerclient.Info, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) ListContainers(all bool, size bool, filters string) ([]dockerclient.Container, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) InspectContainer(id string) (*dockerclient.ContainerInfo, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) InspectImage(id string) (*dockerclient.ImageInfo, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) CreateContainer(config *dockerclient.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (string, error) { + return "", ErrNoEngine +} + +func (client *NopClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan dockerclient.StatsOrError, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) StartContainer(id string, config *dockerclient.HostConfig) error { + return ErrNoEngine +} + +func (client *NopClient) StopContainer(id string, timeout int) error { + return ErrNoEngine +} + +func (client *NopClient) RestartContainer(id string, timeout int) error { + return ErrNoEngine +} + +func (client *NopClient) KillContainer(id, signal string) error { + return ErrNoEngine +} + +func (client *NopClient) Wait(id string) <-chan dockerclient.WaitResult { + return nil +} + +func (client *NopClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) { + return +} + +func (client *NopClient) StopAllMonitorEvents() { + return +} + +func (client *NopClient) TagImage(nameOrID string, repo string, tag string, force bool) error { + return ErrNoEngine +} + +func (client *NopClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) { + return +} + +func (client *NopClient) StopAllMonitorStats() { + return +} + +func (client *NopClient) Version() (*dockerclient.Version, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) PullImage(name string, auth *dockerclient.AuthConfig) error { + return ErrNoEngine +} + +func (client *NopClient) PushImage(name, tag string, auth *dockerclient.AuthConfig) error { + return ErrNoEngine +} + +func (client *NopClient) LoadImage(reader io.Reader) error { + return ErrNoEngine +} + +func (client *NopClient) RemoveContainer(id string, force, volumes bool) error { + return ErrNoEngine +} + +func (client *NopClient) ListImages(all bool) ([]*dockerclient.Image, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) RemoveImage(name string, force bool) ([]*dockerclient.ImageDelete, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) SearchImages(query, registry string, authConfig *dockerclient.AuthConfig) ([]dockerclient.ImageSearch, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) PauseContainer(name string) error { + return ErrNoEngine +} + +func (client *NopClient) UnpauseContainer(name string) error { + return ErrNoEngine +} + +func (client *NopClient) ExecCreate(config *dockerclient.ExecConfig) (string, error) { + return "", ErrNoEngine +} + +func (client *NopClient) ExecStart(id string, config *dockerclient.ExecConfig) error { + return ErrNoEngine +} + +func (client *NopClient) ExecResize(id string, width, height int) error { + return ErrNoEngine +} + +func (client *NopClient) RenameContainer(oldName string, newName string) error { + return ErrNoEngine +} + +func (client *NopClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) BuildImage(image *dockerclient.BuildImage) (io.ReadCloser, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) ListVolumes() ([]*dockerclient.Volume, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) RemoveVolume(name string) error { + return ErrNoEngine +} + +func (client *NopClient) CreateVolume(request *dockerclient.VolumeCreateRequest) (*dockerclient.Volume, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) ListNetworks(filters string) ([]*dockerclient.NetworkResource, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) InspectNetwork(id string) (*dockerclient.NetworkResource, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) CreateNetwork(config *dockerclient.NetworkCreate) (*dockerclient.NetworkCreateResponse, error) { + return nil, ErrNoEngine +} + +func (client *NopClient) ConnectNetwork(id, container string) error { + return ErrNoEngine +} + +func (client *NopClient) DisconnectNetwork(id, container string, force bool) error { + return ErrNoEngine +} + +func (client *NopClient) RemoveNetwork(id string) error { + return ErrNoEngine +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/tls.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/tls.go new file mode 100644 index 00000000..80ae6f1e --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/tls.go @@ -0,0 +1,38 @@ +package dockerclient + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "io/ioutil" + "path/filepath" +) + +// TLSConfigFromCertPath returns a configuration based on PEM files in the directory +// +// path is usually what is set by the environment variable `DOCKER_CERT_PATH`, +// or `$HOME/.docker`. +func TLSConfigFromCertPath(path string) (*tls.Config, error) { + cert, err := ioutil.ReadFile(filepath.Join(path, "cert.pem")) + if err != nil { + return nil, err + } + key, err := ioutil.ReadFile(filepath.Join(path, "key.pem")) + if err != nil { + return nil, err + } + ca, err := ioutil.ReadFile(filepath.Join(path, "ca.pem")) + if err != nil { + return nil, err + } + tlsCert, err := tls.X509KeyPair(cert, key) + if err != nil { + return nil, err + } + tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}} + tlsConfig.RootCAs = x509.NewCertPool() + if !tlsConfig.RootCAs.AppendCertsFromPEM(ca) { + return nil, errors.New("Could not add RootCA pem") + } + return tlsConfig, nil +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/types.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/types.go new file mode 100644 index 00000000..7ba79915 --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/types.go @@ -0,0 +1,624 @@ +package dockerclient + +import ( + "fmt" + "io" + "time" + + "github.com/docker/go-units" +) + +type ContainerConfig struct { + Hostname string + Domainname string + User string + AttachStdin bool + AttachStdout bool + AttachStderr bool + ExposedPorts map[string]struct{} + Tty bool + OpenStdin bool + StdinOnce bool + Env []string + Cmd []string + Image string + Volumes map[string]struct{} + WorkingDir string + Entrypoint []string + NetworkDisabled bool + MacAddress string + OnBuild []string + Labels map[string]string + StopSignal string + + // FIXME: VolumeDriver have been removed since docker 1.9 + VolumeDriver string + + // FIXME: The following fields have been removed since API v1.18 + Memory int64 + MemorySwap int64 + CpuShares int64 + Cpuset string + PortSpecs []string + + // This is used only by the create command + HostConfig HostConfig + + // Network configuration support + NetworkingConfig NetworkingConfig +} + +type HostConfig struct { + Binds []string + ContainerIDFile string + LxcConf []map[string]string + Memory int64 + MemoryReservation int64 + MemorySwap int64 + KernelMemory int64 + CpuShares int64 + CpuPeriod int64 + CpusetCpus string + CpusetMems string + CpuQuota int64 + BlkioWeight int64 + OomKillDisable bool + MemorySwappiness int64 + Privileged bool + PortBindings map[string][]PortBinding + Links []string + PublishAllPorts bool + Dns []string + DNSOptions []string + DnsSearch []string + ExtraHosts []string + VolumesFrom []string + Devices []DeviceMapping + NetworkMode string + IpcMode string + PidMode string + UTSMode string + CapAdd []string + CapDrop []string + GroupAdd []string + RestartPolicy RestartPolicy + SecurityOpt []string + ReadonlyRootfs bool + Ulimits []Ulimit + LogConfig LogConfig + CgroupParent string + ConsoleSize [2]int + VolumeDriver string + OomScoreAdj int + Tmpfs map[string]string + ShmSize int64 + BlkioWeightDevice []WeightDevice + BlkioDeviceReadBps []ThrottleDevice + BlkioDeviceWriteBps []ThrottleDevice + BlkioDeviceReadIOps []ThrottleDevice + BlkioDeviceWriteIOps []ThrottleDevice +} + +type WeightDevice struct { + Path string + Weight uint16 +} + +type ThrottleDevice struct { + Path string + Rate uint64 +} + +type DeviceMapping struct { + PathOnHost string `json:"PathOnHost"` + PathInContainer string `json:"PathInContainer"` + CgroupPermissions string `json:"CgroupPermissions"` +} + +type ExecConfig struct { + AttachStdin bool + AttachStdout bool + AttachStderr bool + Tty bool + Cmd []string + Container string + Detach bool +} + +type LogOptions struct { + Follow bool + Stdout bool + Stderr bool + Timestamps bool + Tail int64 +} + +type AttachOptions struct { + Logs bool + Stream bool + Stdin bool + Stdout bool + Stderr bool +} + +type MonitorEventsFilters struct { + Event string `json:",omitempty"` + Image string `json:",omitempty"` + Container string `json:",omitempty"` +} + +type MonitorEventsOptions struct { + Since int + Until int + Filters *MonitorEventsFilters `json:",omitempty"` +} + +type RestartPolicy struct { + Name string + MaximumRetryCount int64 +} + +type PortBinding struct { + HostIp string + HostPort string +} + +type State struct { + Running bool + Paused bool + Restarting bool + OOMKilled bool + Dead bool + Pid int + ExitCode int + Error string // contains last known error when starting the container + StartedAt time.Time + FinishedAt time.Time + Ghost bool +} + +// String returns a human-readable description of the state +// Stoken from docker/docker/daemon/state.go +func (s *State) String() string { + if s.Running { + if s.Paused { + return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) + } + if s.Restarting { + return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) + } + + return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) + } + + if s.Dead { + return "Dead" + } + + if s.FinishedAt.IsZero() { + return "" + } + + return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) +} + +// StateString returns a single string to describe state +// Stoken from docker/docker/daemon/state.go +func (s *State) StateString() string { + if s.Running { + if s.Paused { + return "paused" + } + if s.Restarting { + return "restarting" + } + return "running" + } + + if s.Dead { + return "dead" + } + + return "exited" +} + +type ImageInfo struct { + Architecture string + Author string + Comment string + Config *ContainerConfig + Container string + ContainerConfig *ContainerConfig + Created time.Time + DockerVersion string + Id string + Os string + Parent string + Size int64 + VirtualSize int64 +} + +type ImageSearch struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty"` + IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty"` +} + +type ContainerInfo struct { + Id string + Created string + Path string + Name string + Args []string + ExecIDs []string + Config *ContainerConfig + State *State + Image string + NetworkSettings struct { + IPAddress string `json:"IpAddress"` + IPPrefixLen int `json:"IpPrefixLen"` + Gateway string + Bridge string + Ports map[string][]PortBinding + Networks map[string]*EndpointSettings + } + SysInitPath string + ResolvConfPath string + Volumes map[string]string + HostConfig *HostConfig +} + +type ContainerChanges struct { + Path string + Kind int +} + +type Port struct { + IP string + PrivatePort int + PublicPort int + Type string +} + +// EndpointSettings stores the network endpoint details +type EndpointSettings struct { + // Configurations + IPAMConfig *EndpointIPAMConfig + Links []string + Aliases []string + // Operational data + NetworkID string + EndpointID string + Gateway string + IPAddress string + IPPrefixLen int + IPv6Gateway string + GlobalIPv6Address string + GlobalIPv6PrefixLen int + MacAddress string +} + +// NetworkingConfig represents the container's networking configuration for each of its interfaces +// Carries the networink configs specified in the `docker run` and `docker network connect` commands +type NetworkingConfig struct { + EndpointsConfig map[string]*EndpointSettings // Endpoint configs for each conencting network +} + +type Container struct { + Id string + Names []string + Image string + Command string + Created int64 + Status string + Ports []Port + SizeRw int64 + SizeRootFs int64 + Labels map[string]string + NetworkSettings struct { + Networks map[string]EndpointSettings + } +} + +type Actor struct { + ID string + Attributes map[string]string +} + +type Event struct { + Status string `json:"status,omitempty"` + ID string `json:"id,omitempty"` + From string `json:"from,omitempty"` + + Type string + Action string + Actor Actor + + Time int64 `json:"time,omitempty"` + TimeNano int64 `json:"timeNano,omitempty"` +} + +type Version struct { + ApiVersion string + Arch string + GitCommit string + GoVersion string + KernelVersion string + Os string + Version string +} + +type RespContainersCreate struct { + Id string + Warnings []string +} + +type Image struct { + Created int64 + Id string + Labels map[string]string + ParentId string + RepoDigests []string + RepoTags []string + Size int64 + VirtualSize int64 +} + +// Info is the struct returned by /info +// The API is currently in flux, so Debug, MemoryLimit, SwapLimit, and +// IPv4Forwarding are interfaces because in docker 1.6.1 they are 0 or 1 but in +// master they are bools. +type Info struct { + ID string + Containers int64 + Driver string + DriverStatus [][]string + ExecutionDriver string + Images int64 + KernelVersion string + OperatingSystem string + NCPU int64 + MemTotal int64 + Name string + Labels []string + Debug interface{} + NFd int64 + NGoroutines int64 + SystemTime string + NEventsListener int64 + InitPath string + InitSha1 string + IndexServerAddress string + MemoryLimit interface{} + SwapLimit interface{} + IPv4Forwarding interface{} + BridgeNfIptables bool + BridgeNfIp6tables bool + DockerRootDir string + HttpProxy string + HttpsProxy string + NoProxy string +} + +type ImageDelete struct { + Deleted string + Untagged string +} + +type StatsOrError struct { + Stats + Error error +} + +type EventOrError struct { + Event + Error error +} + +type WaitResult struct { + ExitCode int + Error error +} + +type decodingResult struct { + result interface{} + err error +} + +// The following are types for the API stats endpoint +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 NetworkStats 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 MemoryStats struct { + Usage uint64 `json:"usage"` + MaxUsage uint64 `json:"max_usage"` + Stats map[string]uint64 `json:"stats"` + 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 Stats struct { + Read time.Time `json:"read"` + NetworkStats NetworkStats `json:"network,omitempty"` + CpuStats CpuStats `json:"cpu_stats,omitempty"` + MemoryStats MemoryStats `json:"memory_stats,omitempty"` + BlkioStats BlkioStats `json:"blkio_stats,omitempty"` +} + +type Ulimit struct { + Name string `json:"name"` + Soft uint64 `json:"soft"` + Hard uint64 `json:"hard"` +} + +type LogConfig struct { + Type string `json:"type"` + Config map[string]string `json:"config"` +} + +type BuildImage struct { + Config *ConfigFile + DockerfileName string + Context io.Reader + RemoteURL string + RepoName string + SuppressOutput bool + NoCache bool + Remove bool + ForceRemove bool + Pull bool + Memory int64 + MemorySwap int64 + CpuShares int64 + CpuPeriod int64 + CpuQuota int64 + CpuSetCpus string + CpuSetMems string + CgroupParent string + BuildArgs map[string]string +} + +type Volume struct { + Name string // Name is the name of the volume + Driver string // Driver is the Driver name used to create the volume + Mountpoint string // Mountpoint is the location on disk of the volume +} + +type VolumesListResponse struct { + Volumes []*Volume // Volumes is the list of volumes being returned +} + +type VolumeCreateRequest struct { + Name string // Name is the requested name of the volume + Driver string // Driver is the name of the driver that should be used to create the volume + DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume. +} + +// IPAM represents IP Address Management +type IPAM struct { + Driver string + Options map[string]string //Per network IPAM driver options + Config []IPAMConfig +} + +// IPAMConfig represents IPAM configurations +type IPAMConfig struct { + Subnet string `json:",omitempty"` + IPRange string `json:",omitempty"` + Gateway string `json:",omitempty"` + AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"` +} + +// EndpointIPAMConfig represents IPAM configurations for the endpoint +type EndpointIPAMConfig struct { + IPv4Address string `json:",omitempty"` + IPv6Address string `json:",omitempty"` +} + +// NetworkResource is the body of the "get network" http response message +type NetworkResource struct { + Name string + ID string `json:"Id"` + Scope string + Driver string + IPAM IPAM + //Internal bool + Containers map[string]EndpointResource + Options map[string]string +} + +// EndpointResource contains network resources allocated and used for a container in a network +type EndpointResource struct { + Name string + EndpointID string + MacAddress string + IPv4Address string + IPv6Address string +} + +// NetworkCreate is the expected body of the "create network" http request message +type NetworkCreate struct { + Name string + CheckDuplicate bool + Driver string + IPAM IPAM + Internal bool + Options map[string]string +} + +// NetworkCreateResponse is the response message sent by the server for network create call +type NetworkCreateResponse struct { + ID string `json:"Id"` + Warning string +} + +// NetworkConnect represents the data to be used to connect a container to the network +type NetworkConnect struct { + Container string +} + +// NetworkDisconnect represents the data to be used to disconnect a container from the network +type NetworkDisconnect struct { + Container string + Force bool +} diff --git a/go/src/gosetup/vendor/github.com/samalba/dockerclient/utils.go b/go/src/gosetup/vendor/github.com/samalba/dockerclient/utils.go new file mode 100644 index 00000000..8a6b0d6e --- /dev/null +++ b/go/src/gosetup/vendor/github.com/samalba/dockerclient/utils.go @@ -0,0 +1,41 @@ +package dockerclient + +import ( + "crypto/tls" + "net" + "net/http" + "net/url" + "time" +) + +type tcpFunc func(*net.TCPConn, time.Duration) error + +func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration, setUserTimeout tcpFunc) *http.Client { + httpTransport := &http.Transport{ + TLSClientConfig: tlsConfig, + } + + switch u.Scheme { + default: + httpTransport.Dial = func(proto, addr string) (net.Conn, error) { + conn, err := net.DialTimeout(proto, addr, timeout) + if tcpConn, ok := conn.(*net.TCPConn); ok && setUserTimeout != nil { + // Sender can break TCP connection if the remote side doesn't + // acknowledge packets within timeout + setUserTimeout(tcpConn, timeout) + } + return conn, err + } + case "unix": + socketPath := u.Path + unixDial := func(proto, addr string) (net.Conn, error) { + return net.DialTimeout("unix", socketPath, timeout) + } + httpTransport.Dial = unixDial + // Override the main URL object so the HTTP lib won't complain + u.Scheme = "http" + u.Host = "unix.sock" + u.Path = "" + } + return &http.Client{Transport: httpTransport} +} diff --git a/start.sh b/start.sh index 73c9632b..26dae3fa 100755 --- a/start.sh +++ b/start.sh @@ -6,4 +6,9 @@ goproxy &> /srv/world/goproxy_out & # start Minecraft C++ server cd /srv/world + +# GOPROXY DEVELOPMENT: start cuberite as a daemon +#../cuberite_server/Cuberite -d ../cuberite_server/Cuberite + +/bin/bash \ No newline at end of file diff --git a/world/Plugins/Docker/container.lua b/world/Plugins/Docker/container.lua index 8bf8e689..1f624876 100644 --- a/world/Plugins/Docker/container.lua +++ b/world/Plugins/Docker/container.lua @@ -11,6 +11,7 @@ CONTAINER_STOPPED = 2 -- the Minecraft world function NewContainer() c = { + -- attributes displayed = false, x = 0, z = 0, @@ -19,18 +20,31 @@ function NewContainer() imageRepo="", imageTag="", running=false, + networks={}, -- array of network objects + -- methods init=Container.init, setInfos=Container.setInfos, destroy=Container.destroy, display=Container.display, updateMemSign=Container.updateMemSign, updateCPUSign=Container.updateCPUSign, - addGround=Container.addGround + addGround=Container.addGround, + connectNetwork=Container.connectNetwork } return c end -Container = {displayed = false, x = 0, z = 0, name="",id="",imageRepo="",imageTag="",running=false} +Container = { + displayed = false, + x = 0, + z = 0, + name="", + id="", + imageRepo="", + imageTag="", + running=false, + networks={} + } -- Container:init sets Container's position function Container:init(x,z) @@ -152,7 +166,6 @@ function Container:display(running) setBlock(UpdateQueue,self.x+1,GROUND_LEVEL+3,self.z+1,E_BLOCK_LEVER,9) end - -- remove button setBlock(UpdateQueue,self.x+2,GROUND_LEVEL + 3,self.z + 2,E_BLOCK_WALLSIGN,E_META_CHEST_FACING_XM) @@ -160,12 +173,10 @@ function Container:display(running) setBlock(UpdateQueue,self.x+2,GROUND_LEVEL+3,self.z+3,E_BLOCK_STONE_BUTTON,E_BLOCK_BUTTON_XM) - -- door -- Cuberite bug with Minecraft 1.8 apparently, doors are not displayed correctly -- setBlock(UpdateQueue,self.x+2,GROUND_LEVEL+2,self.z,E_BLOCK_WOODEN_DOOR,E_META_CHEST_FACING_ZM) - for px=self.x, self.x+3 do for pz=self.z, self.z+4 @@ -213,3 +224,23 @@ function Container:addGround() end end end + +-- Container:connectNetwork +function Container:connectNetwork(network) + -- check if the container is already connected to this network + found = false + for i=1, table.getn(self.networks) do + if self.networks[i].id == network.id then + found = true + break + end + end + if found == false then + -- TODO: gdevillele: support multiple networks per container + LOG("[CONTAINER] [connectNetwork] container is connected to a new network") + table.insert(self.networks, network) + setBlock(UpdateQueue, self.x+3, GROUND_LEVEL+5, self.z, E_BLOCK_WOOL, E_META_WOOL_RED) + else + -- container already know it is connected to this network, we do noting + end +end diff --git a/world/Plugins/Docker/docker.lua b/world/Plugins/Docker/docker.lua index a87e2d19..5a2e8af6 100644 --- a/world/Plugins/Docker/docker.lua +++ b/world/Plugins/Docker/docker.lua @@ -10,6 +10,9 @@ SignsToUpdate = {} -- in the "Containers" array to indicate that there is no container at an index EmptyContainerSpace = {} +-- array of docker networks objects +Networks = {} + -- Tick is triggered by cPluginManager.HOOK_TICK function Tick(TimeDelta) UpdateQueue:update(MAX_BLOCK_UPDATE_PER_TICK) @@ -17,13 +20,12 @@ end -- Plugin initialization function Initialize(Plugin) - Plugin:SetName("Docker") + Plugin:SetName("Docker") -- set the name of the plugin Plugin:SetVersion(1) UpdateQueue = NewUpdateQueue() -- Hooks - cPluginManager:AddHook(cPluginManager.HOOK_WORLD_STARTED, WorldStarted); cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_JOINED, PlayerJoined); cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, PlayerUsingBlock); @@ -35,17 +37,14 @@ function Initialize(Plugin) cPluginManager:AddHook(cPluginManager.HOOK_TICK, Tick); -- Command Bindings - cPluginManager.BindCommand("/docker", "*", DockerCommand, " - docker CLI commands") - Plugin:AddWebTab("Docker",HandleRequest_Docker) -- make all players admin cRankManager:SetDefaultRank("Admin") - - LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) - + LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() .. " plugin") + -- return true to tell Cuberite the plugin has been initialized successfully return true end @@ -124,6 +123,40 @@ function destroyContainer(id) end end +-- updateNetwork +function updateNetwork(containerId, networkId, networkName, networkType) + LOG("[UPDATE NETWORK] network " .. networkName .. " (" .. networkId .. ") (" .. networkType .. ") now contains container " .. containerId) + + -- create network object based on the function arguments + network = NewNetwork() + network:init(networkId, networkName, networkType) + + -- first: check whether this network is already known + networkFound = false + for i=1, table.getn(Networks) do + -- if network is found + if Networks[i].id == networkId then + networkFound = true + LOG("[UPDATE NETWORK] network has been found") + break + end + end + + -- second: if network is not found, we add it + if networkFound == false then + LOG("[UPDATE NETWORK] network has NOT been found, we add it to the list of networks") + table.insert(Networks, network) + end + + -- third: update the container so it knows it is part of the network + for i=1, table.getn(Containers) do + if Containers[i] ~= EmptyContainerSpace and Containers[i].id == containerId then + -- tell container object, it is connected to a network + Containers[i]:connectNetwork(network) + end + end +end + -- updateContainer accepts 3 different states: running, stopped, created -- sometimes "start" events arrive before "create" ones -- in this case, we just ignore the update @@ -290,17 +323,29 @@ function DockerCommand(Split, Player) end - +-- handle http requests received from the dockercraft goproxy function HandleRequest_Docker(Request) - + content = "[dockerclient]" + -- test if the request contains an "action" field if Request.PostParams["action"] ~= nil then + -- retrieving the value of the "action" field action = Request.PostParams["action"] + -- network actions + if action == "network_connect" + then + containerId = Request.PostParams["containerId"] + networkId = Request.PostParams["networkId"] + networkName = Request.PostParams["networkName"] + networkType = Request.PostParams["networkType"] + LOG("EVENT - network_connect "..containerId.." "..networkId.." "..networkName.." "..networkType) + updateNetwork(containerId, networkId, networkName, networkType) + end + -- receiving informations about one container - if action == "containerInfos" then LOG("EVENT - containerInfos") @@ -373,12 +418,10 @@ function HandleRequest_Docker(Request) updateStats(id,ram,cpu) end - content = content .. "{action:\"" .. action .. "\"}" else content = content .. "{error:\"action requested\"}" - end content = content .. "[/dockerclient]" diff --git a/world/Plugins/Docker/network.lua b/world/Plugins/Docker/network.lua new file mode 100644 index 00000000..b8abeb60 --- /dev/null +++ b/world/Plugins/Docker/network.lua @@ -0,0 +1,210 @@ +-- Network object is the representation of a Docker network +-- in the Minecraft world + +-- NewNetwork returns a Network object, +-- representing of a Docker network in +-- the Minecraft world +function NewNetwork() + c = { + id="", + name="", + type="", + -- methods + init=Network.init, + -- setInfos=Container.setInfos, + -- destroy=Container.destroy, + -- display=Container.display, + -- updateMemSign=Container.updateMemSign, + -- updateCPUSign=Container.updateCPUSign, + -- addGround=Container.addGround + } + return c +end + +Network = { + id="", + name="", + type="" + } + +-- Network:init sets Network's properties +function Network:init(id,name,type) + self.id = id + self.name = name + self.type = type +end + +-- -- Container:setInfos sets Container's id, name, imageRepo, +-- -- image tag and running state +-- function Container:setInfos(id,name,imageRepo,imageTag,running) +-- self.id = id +-- self.name = name +-- self.imageRepo = imageRepo +-- self.imageTag = imageTag +-- self.running = running +-- end + +-- -- Container:destroy removes all blocks of the +-- -- container, it won't be visible on the map anymore +-- function Container:destroy(running) +-- X = self.x+2 +-- Y = GROUND_LEVEL+2 +-- Z = self.z+2 +-- LOG("Exploding at X:" .. X .. " Y:" .. Y .. " Z:" .. Z) +-- local World = cRoot:Get():GetDefaultWorld() +-- World:BroadcastSoundEffect("random.explode", X, Y, Z, 1, 1) +-- World:BroadcastParticleEffect("hugeexplosion",X, Y, Z, 0, 0, 0, 1, 1) + +-- -- if a block is removed before it's button/lever/sign, that object will drop +-- -- and the player can collect it. Remove these first + +-- -- lever +-- digBlock(UpdateQueue,self.x+1,GROUND_LEVEL+3,self.z+1) +-- -- signs +-- digBlock(UpdateQueue,self.x+3,GROUND_LEVEL+2,self.z-1) +-- digBlock(UpdateQueue,self.x,GROUND_LEVEL+2,self.z-1) +-- digBlock(UpdateQueue,self.x+1,GROUND_LEVEL+2,self.z-1) +-- -- torch +-- digBlock(UpdateQueue,self.x+1,GROUND_LEVEL+3,self.z+1) +-- --button +-- digBlock(UpdateQueue,self.x+2,GROUND_LEVEL+3,self.z+2) + +-- -- rest of the blocks +-- for py = GROUND_LEVEL+1, GROUND_LEVEL+4 +-- do +-- for px=self.x-1, self.x+4 +-- do +-- for pz=self.z-1, self.z+5 +-- do +-- digBlock(UpdateQueue,px,py,pz) +-- end +-- end +-- end +-- end + +-- -- Container:display displays all Container's blocks +-- -- Blocks will be blue if the container is running, +-- -- orange otherwise. +-- function Container:display(running) + +-- metaPrimaryColor = E_META_WOOL_LIGHTBLUE +-- metaSecondaryColor = E_META_WOOL_BLUE + +-- if running == false +-- then +-- metaPrimaryColor = E_META_WOOL_ORANGE +-- metaSecondaryColor = E_META_WOOL_RED +-- end + +-- self.displayed = true + +-- for px=self.x, self.x+3 +-- do +-- for pz=self.z, self.z+4 +-- do +-- setBlock(UpdateQueue,px,GROUND_LEVEL + 1,pz,E_BLOCK_WOOL,metaPrimaryColor) +-- end +-- end + +-- for py = GROUND_LEVEL+2, GROUND_LEVEL+3 +-- do +-- setBlock(UpdateQueue,self.x+1,py,self.z,E_BLOCK_WOOL,metaPrimaryColor) + +-- -- leave empty space for the door +-- -- setBlock(UpdateQueue,self.x+2,py,self.z,E_BLOCK_WOOL,metaPrimaryColor) + +-- setBlock(UpdateQueue,self.x,py,self.z,E_BLOCK_WOOL,metaPrimaryColor) +-- setBlock(UpdateQueue,self.x+3,py,self.z,E_BLOCK_WOOL,metaPrimaryColor) + +-- setBlock(UpdateQueue,self.x,py,self.z+1,E_BLOCK_WOOL,metaSecondaryColor) +-- setBlock(UpdateQueue,self.x+3,py,self.z+1,E_BLOCK_WOOL,metaSecondaryColor) + +-- setBlock(UpdateQueue,self.x,py,self.z+2,E_BLOCK_WOOL,metaPrimaryColor) +-- setBlock(UpdateQueue,self.x+3,py,self.z+2,E_BLOCK_WOOL,metaPrimaryColor) + +-- setBlock(UpdateQueue,self.x,py,self.z+3,E_BLOCK_WOOL,metaSecondaryColor) +-- setBlock(UpdateQueue,self.x+3,py,self.z+3,E_BLOCK_WOOL,metaSecondaryColor) + +-- setBlock(UpdateQueue,self.x,py,self.z+4,E_BLOCK_WOOL,metaPrimaryColor) +-- setBlock(UpdateQueue,self.x+3,py,self.z+4,E_BLOCK_WOOL,metaPrimaryColor) + +-- setBlock(UpdateQueue,self.x+1,py,self.z+4,E_BLOCK_WOOL,metaPrimaryColor) +-- setBlock(UpdateQueue,self.x+2,py,self.z+4,E_BLOCK_WOOL,metaPrimaryColor) +-- end + +-- -- torch +-- setBlock(UpdateQueue,self.x+1,GROUND_LEVEL+3,self.z+3,E_BLOCK_TORCH,E_META_TORCH_ZP) + +-- -- start / stop lever +-- setBlock(UpdateQueue,self.x+1,GROUND_LEVEL + 3,self.z + 2,E_BLOCK_WALLSIGN,E_META_CHEST_FACING_XP) +-- updateSign(UpdateQueue,self.x+1,GROUND_LEVEL + 3,self.z + 2,"","START/STOP","---->","",2) + + +-- if running +-- then +-- setBlock(UpdateQueue,self.x+1,GROUND_LEVEL+3,self.z+1,E_BLOCK_LEVER,1) +-- else +-- setBlock(UpdateQueue,self.x+1,GROUND_LEVEL+3,self.z+1,E_BLOCK_LEVER,9) +-- end + + +-- -- remove button + +-- setBlock(UpdateQueue,self.x+2,GROUND_LEVEL + 3,self.z + 2,E_BLOCK_WALLSIGN,E_META_CHEST_FACING_XM) +-- updateSign(UpdateQueue,self.x+2,GROUND_LEVEL + 3,self.z + 2,"","REMOVE","---->","",2) + +-- setBlock(UpdateQueue,self.x+2,GROUND_LEVEL+3,self.z+3,E_BLOCK_STONE_BUTTON,E_BLOCK_BUTTON_XM) + + +-- -- door +-- -- Cuberite bug with Minecraft 1.8 apparently, doors are not displayed correctly +-- -- setBlock(UpdateQueue,self.x+2,GROUND_LEVEL+2,self.z,E_BLOCK_WOODEN_DOOR,E_META_CHEST_FACING_ZM) + + +-- for px=self.x, self.x+3 +-- do +-- for pz=self.z, self.z+4 +-- do +-- setBlock(UpdateQueue,px,GROUND_LEVEL + 4,pz,E_BLOCK_WOOL,metaPrimaryColor) +-- end +-- end + +-- setBlock(UpdateQueue,self.x+3,GROUND_LEVEL + 2,self.z - 1,E_BLOCK_WALLSIGN,E_META_CHEST_FACING_ZM) +-- updateSign(UpdateQueue,self.x+3,GROUND_LEVEL + 2,self.z - 1,string.sub(self.id,1,8),self.name,self.imageRepo,self.imageTag,2) + +-- -- Mem sign +-- setBlock(UpdateQueue,self.x,GROUND_LEVEL + 2,self.z - 1,E_BLOCK_WALLSIGN,E_META_CHEST_FACING_ZM) + +-- -- CPU sign +-- setBlock(UpdateQueue,self.x+1,GROUND_LEVEL + 2,self.z - 1,E_BLOCK_WALLSIGN,E_META_CHEST_FACING_ZM) +-- end + + +-- -- Container:updateMemSign updates the mem usage +-- -- value displayed on Container's sign +-- function Container:updateMemSign(s) +-- updateSign(UpdateQueue,self.x,GROUND_LEVEL + 2,self.z - 1,"Mem usage","",s,"") +-- end + +-- -- Container:updateCPUSign updates the mem usage +-- -- value displayed on Container's sign +-- function Container:updateCPUSign(s) +-- updateSign(UpdateQueue,self.x+1,GROUND_LEVEL + 2,self.z - 1,"CPU usage","",s,"") +-- end + +-- -- Container:addGround creates ground blocks +-- -- necessary to display the container +-- function Container:addGround() +-- if GROUND_MIN_X > self.x - 2 +-- then +-- OLD_GROUND_MIN_X = GROUND_MIN_X +-- GROUND_MIN_X = self.x - 2 +-- for x= GROUND_MIN_X, OLD_GROUND_MIN_X +-- do +-- for z=GROUND_MIN_Z,GROUND_MAX_Z +-- do +-- setBlock(UpdateQueue,x,y,z,E_BLOCK_WOOL,E_META_WOOL_WHITE) +-- end +-- end +-- end +-- end