├── .gitignore ├── Dockerfile ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── github.com │ ├── Sirupsen │ │ └── logrus │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── entry.go │ │ │ ├── entry_test.go │ │ │ ├── examples │ │ │ ├── basic │ │ │ │ └── basic.go │ │ │ └── hook │ │ │ │ └── hook.go │ │ │ ├── exported.go │ │ │ ├── formatter.go │ │ │ ├── formatter_bench_test.go │ │ │ ├── formatters │ │ │ └── logstash │ │ │ │ ├── logstash.go │ │ │ │ └── logstash_test.go │ │ │ ├── hook_test.go │ │ │ ├── hooks.go │ │ │ ├── hooks │ │ │ ├── airbrake │ │ │ │ ├── airbrake.go │ │ │ │ └── airbrake_test.go │ │ │ ├── bugsnag │ │ │ │ ├── bugsnag.go │ │ │ │ └── bugsnag_test.go │ │ │ ├── papertrail │ │ │ │ ├── README.md │ │ │ │ ├── papertrail.go │ │ │ │ └── papertrail_test.go │ │ │ ├── sentry │ │ │ │ ├── README.md │ │ │ │ ├── sentry.go │ │ │ │ └── sentry_test.go │ │ │ └── syslog │ │ │ │ ├── README.md │ │ │ │ ├── syslog.go │ │ │ │ └── syslog_test.go │ │ │ ├── json_formatter.go │ │ │ ├── json_formatter_test.go │ │ │ ├── logger.go │ │ │ ├── logrus.go │ │ │ ├── logrus_test.go │ │ │ ├── terminal_darwin.go │ │ │ ├── terminal_freebsd.go │ │ │ ├── terminal_linux.go │ │ │ ├── terminal_notwindows.go │ │ │ ├── terminal_openbsd.go │ │ │ ├── terminal_windows.go │ │ │ ├── text_formatter.go │ │ │ ├── text_formatter_test.go │ │ │ └── writer.go │ ├── docker │ │ ├── distribution │ │ │ ├── LICENSE │ │ │ ├── digest │ │ │ │ ├── digest.go │ │ │ │ ├── digester.go │ │ │ │ ├── doc.go │ │ │ │ ├── set.go │ │ │ │ └── verifiers.go │ │ │ └── reference │ │ │ │ ├── reference.go │ │ │ │ └── regexp.go │ │ ├── docker │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ └── pkg │ │ │ │ ├── fileutils │ │ │ │ ├── fileutils.go │ │ │ │ ├── fileutils_unix.go │ │ │ │ └── fileutils_windows.go │ │ │ │ ├── ioutils │ │ │ │ ├── bytespipe.go │ │ │ │ ├── fmt.go │ │ │ │ ├── multireader.go │ │ │ │ ├── readers.go │ │ │ │ ├── scheduler.go │ │ │ │ ├── scheduler_gccgo.go │ │ │ │ ├── temp_unix.go │ │ │ │ ├── temp_windows.go │ │ │ │ ├── writeflusher.go │ │ │ │ └── writers.go │ │ │ │ ├── longpath │ │ │ │ └── longpath.go │ │ │ │ ├── pools │ │ │ │ └── pools.go │ │ │ │ ├── stdcopy │ │ │ │ └── stdcopy.go │ │ │ │ ├── system │ │ │ │ ├── chtimes.go │ │ │ │ ├── errors.go │ │ │ │ ├── events_windows.go │ │ │ │ ├── filesys.go │ │ │ │ ├── filesys_windows.go │ │ │ │ ├── lstat.go │ │ │ │ ├── lstat_windows.go │ │ │ │ ├── meminfo.go │ │ │ │ ├── meminfo_linux.go │ │ │ │ ├── meminfo_unsupported.go │ │ │ │ ├── meminfo_windows.go │ │ │ │ ├── mknod.go │ │ │ │ ├── mknod_windows.go │ │ │ │ ├── path_unix.go │ │ │ │ ├── path_windows.go │ │ │ │ ├── stat.go │ │ │ │ ├── stat_freebsd.go │ │ │ │ ├── stat_linux.go │ │ │ │ ├── stat_solaris.go │ │ │ │ ├── stat_unsupported.go │ │ │ │ ├── stat_windows.go │ │ │ │ ├── syscall_unix.go │ │ │ │ ├── syscall_windows.go │ │ │ │ ├── umask.go │ │ │ │ ├── umask_windows.go │ │ │ │ ├── utimes_darwin.go │ │ │ │ ├── utimes_freebsd.go │ │ │ │ ├── utimes_linux.go │ │ │ │ ├── utimes_unsupported.go │ │ │ │ ├── xattrs_linux.go │ │ │ │ └── xattrs_unsupported.go │ │ │ │ └── units │ │ │ │ ├── duration.go │ │ │ │ ├── duration_test.go │ │ │ │ ├── size.go │ │ │ │ └── size_test.go │ │ └── go-units │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE.code │ │ │ ├── LICENSE.docs │ │ │ ├── MAINTAINERS │ │ │ ├── README.md │ │ │ ├── circle.yml │ │ │ ├── duration.go │ │ │ ├── size.go │ │ │ └── ulimit.go │ ├── jlhawn │ │ └── tarsum │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── MAINTAINERS │ │ │ ├── README.md │ │ │ ├── archive │ │ │ └── tar │ │ │ │ ├── common.go │ │ │ │ ├── reader.go │ │ │ │ ├── stat_atim.go │ │ │ │ ├── stat_atimespec.go │ │ │ │ ├── stat_unix.go │ │ │ │ └── writer.go │ │ │ ├── fileinfosums.go │ │ │ ├── fileinfosums_test.go │ │ │ ├── sha256 │ │ │ ├── sha256.go │ │ │ ├── sha256block.go │ │ │ ├── sha256block_386.s │ │ │ ├── sha256block_amd64.s │ │ │ └── sha256block_decl.go │ │ │ ├── tarsum.go │ │ │ ├── tarsum_test.go │ │ │ ├── tarsumdigest.go │ │ │ ├── tarsumdigest_test.go │ │ │ ├── testdata │ │ │ ├── 46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457 │ │ │ │ ├── json │ │ │ │ └── layer.tar │ │ │ ├── 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158 │ │ │ │ ├── json │ │ │ │ └── layer.tar │ │ │ ├── collision │ │ │ │ ├── collision-0.tar │ │ │ │ ├── collision-1.tar │ │ │ │ ├── collision-2.tar │ │ │ │ └── collision-3.tar │ │ │ └── xattr │ │ │ │ ├── json │ │ │ │ └── layer.tar │ │ │ ├── versioning.go │ │ │ ├── versioning_test.go │ │ │ └── writercloser.go │ └── samalba │ │ └── dockerclient │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── auth.go │ │ ├── dockerclient.go │ │ ├── example_responses.go │ │ ├── interface.go │ │ ├── tls.go │ │ ├── types.go │ │ └── utils.go │ └── golang.org │ └── x │ └── net │ ├── LICENSE │ ├── PATENTS │ └── context │ ├── context.go │ ├── go17.go │ └── pre_go17.go ├── LICENSE ├── Makefile ├── README.md ├── archive ├── archive.go ├── archive_unix.go ├── archive_windows.go ├── copy.go ├── testdata │ └── broken.tar ├── time_linux.go └── time_unsupported.go ├── build ├── builder.go ├── cache.go ├── commands │ └── command.go ├── commit.go ├── copy.go ├── extract.go ├── from.go ├── handlers.go ├── hijack.go ├── parser │ ├── cmd │ │ └── parse_dockerfile │ │ │ └── main.go │ ├── lexer.go │ ├── parser.go │ └── token.go ├── run.go ├── shell_parser.go ├── shell_parser_test.go ├── tag.go └── words ├── cmd └── dockramp │ └── main.go ├── dockramp.sublime-project ├── dockramp.sublime-workspace └── make_binaries.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | bundles 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | 27 | # Editor/IDE specific files. 28 | *.sublime-project 29 | *.sublime-workspace 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.4.2-cross 2 | 3 | MAINTAINER 'Josh Hawn (github:jlhawn)' 4 | 5 | ENV PROJ_DIR /go/src/github.com/jlhawn/dockramp 6 | 7 | ENV PLATFORMS ' 8 | darwin/386 darwin/amd64 9 | freebsd/386 freebsd/amd64 freebsd/arm 10 | linux/386 linux/amd64 linux/arm 11 | windows/386 windows/amd64 12 | ' 13 | 14 | RUN sh -c 'mkdir -p $PROJ_DIR' 15 | 16 | COPY . $PROJ_DIR 17 | 18 | RUN sh -c 'cp "$PROJ_DIR/make_binaries.sh" /usr/local/bin/make_binaries.sh' 19 | 20 | ENTRYPOINT /usr/local/bin/make_binaries.sh 21 | CMD /bundles 22 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/jlhawn/dockramp", 3 | "GoVersion": "go1.4.2", 4 | "GodepVersion": "v62", 5 | "Packages": [ 6 | "./..." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/Sirupsen/logrus", 11 | "Comment": "v0.8.2-12-g2a0a9a1", 12 | "Rev": "2a0a9a12ae2892ccfa9ab59c1222e9dcd29f9367" 13 | }, 14 | { 15 | "ImportPath": "github.com/docker/distribution/digest", 16 | "Comment": "v2.3.0-rc.2-247-g6a992e1", 17 | "Rev": "6a992e1348f69d77da2131cd0d7388073489098b" 18 | }, 19 | { 20 | "ImportPath": "github.com/docker/distribution/reference", 21 | "Comment": "v2.3.0-rc.2-247-g6a992e1", 22 | "Rev": "6a992e1348f69d77da2131cd0d7388073489098b" 23 | }, 24 | { 25 | "ImportPath": "github.com/docker/docker/pkg/fileutils", 26 | "Comment": "v1.10.2", 27 | "Rev": "c3959b140f33c115c50710d2944dbf7e211bacd0" 28 | }, 29 | { 30 | "ImportPath": "github.com/docker/docker/pkg/ioutils", 31 | "Comment": "v1.10.2", 32 | "Rev": "c3959b140f33c115c50710d2944dbf7e211bacd0" 33 | }, 34 | { 35 | "ImportPath": "github.com/docker/docker/pkg/longpath", 36 | "Comment": "v1.10.2", 37 | "Rev": "c3959b140f33c115c50710d2944dbf7e211bacd0" 38 | }, 39 | { 40 | "ImportPath": "github.com/docker/docker/pkg/pools", 41 | "Comment": "v1.10.2", 42 | "Rev": "c3959b140f33c115c50710d2944dbf7e211bacd0" 43 | }, 44 | { 45 | "ImportPath": "github.com/docker/docker/pkg/stdcopy", 46 | "Comment": "v1.10.2", 47 | "Rev": "c3959b140f33c115c50710d2944dbf7e211bacd0" 48 | }, 49 | { 50 | "ImportPath": "github.com/docker/docker/pkg/system", 51 | "Comment": "v1.10.2", 52 | "Rev": "c3959b140f33c115c50710d2944dbf7e211bacd0" 53 | }, 54 | { 55 | "ImportPath": "github.com/docker/go-units", 56 | "Comment": "v0.3.0", 57 | "Rev": "5d2041e26a699eaca682e2ea41c8f891e1060444" 58 | }, 59 | { 60 | "ImportPath": "github.com/jlhawn/tarsum", 61 | "Rev": "d07f381518d81a1043f9f1f6e272101b52b85970" 62 | }, 63 | { 64 | "ImportPath": "github.com/jlhawn/tarsum/archive/tar", 65 | "Rev": "d07f381518d81a1043f9f1f6e272101b52b85970" 66 | }, 67 | { 68 | "ImportPath": "github.com/jlhawn/tarsum/sha256", 69 | "Rev": "d07f381518d81a1043f9f1f6e272101b52b85970" 70 | }, 71 | { 72 | "ImportPath": "github.com/samalba/dockerclient", 73 | "Rev": "91d7393ff85980ba3a8966405871a3d446ca28f2" 74 | }, 75 | { 76 | "ImportPath": "golang.org/x/net/context", 77 | "Rev": "fb93926129b8ec0056f2f458b1f519654814edf0" 78 | } 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore: -------------------------------------------------------------------------------- 1 | logrus 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.2 4 | - 1.3 5 | - 1.4 6 | - tip 7 | install: 8 | - go get -t ./... 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.8.2 2 | 3 | logrus: fix more Fatal family functions 4 | 5 | # 0.8.1 6 | 7 | logrus: fix not exiting on `Fatalf` and `Fatalln` 8 | 9 | # 0.8.0 10 | 11 | logrus: defaults to stderr instead of stdout 12 | hooks/sentry: add special field for `*http.Request` 13 | formatter/text: ignore Windows for colors 14 | 15 | # 0.7.3 16 | 17 | formatter/\*: allow configuration of timestamp layout 18 | 19 | # 0.7.2 20 | 21 | formatter/text: Add configuration option for time format (#158) 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Simon Eskildsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEntryPanicln(t *testing.T) { 12 | errBoom := fmt.Errorf("boom time") 13 | 14 | defer func() { 15 | p := recover() 16 | assert.NotNil(t, p) 17 | 18 | switch pVal := p.(type) { 19 | case *Entry: 20 | assert.Equal(t, "kaboom", pVal.Message) 21 | assert.Equal(t, errBoom, pVal.Data["err"]) 22 | default: 23 | t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) 24 | } 25 | }() 26 | 27 | logger := New() 28 | logger.Out = &bytes.Buffer{} 29 | entry := NewEntry(logger) 30 | entry.WithField("err", errBoom).Panicln("kaboom") 31 | } 32 | 33 | func TestEntryPanicf(t *testing.T) { 34 | errBoom := fmt.Errorf("boom again") 35 | 36 | defer func() { 37 | p := recover() 38 | assert.NotNil(t, p) 39 | 40 | switch pVal := p.(type) { 41 | case *Entry: 42 | assert.Equal(t, "kaboom true", pVal.Message) 43 | assert.Equal(t, errBoom, pVal.Data["err"]) 44 | default: 45 | t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) 46 | } 47 | }() 48 | 49 | logger := New() 50 | logger.Out = &bytes.Buffer{} 51 | entry := NewEntry(logger) 52 | entry.WithField("err", errBoom).Panicf("kaboom %v", true) 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | ) 6 | 7 | var log = logrus.New() 8 | 9 | func init() { 10 | log.Formatter = new(logrus.JSONFormatter) 11 | log.Formatter = new(logrus.TextFormatter) // default 12 | log.Level = logrus.DebugLevel 13 | } 14 | 15 | func main() { 16 | defer func() { 17 | err := recover() 18 | if err != nil { 19 | log.WithFields(logrus.Fields{ 20 | "omg": true, 21 | "err": err, 22 | "number": 100, 23 | }).Fatal("The ice breaks!") 24 | } 25 | }() 26 | 27 | log.WithFields(logrus.Fields{ 28 | "animal": "walrus", 29 | "number": 8, 30 | }).Debug("Started observing beach") 31 | 32 | log.WithFields(logrus.Fields{ 33 | "animal": "walrus", 34 | "size": 10, 35 | }).Info("A group of walrus emerges from the ocean") 36 | 37 | log.WithFields(logrus.Fields{ 38 | "omg": true, 39 | "number": 122, 40 | }).Warn("The group's number increased tremendously!") 41 | 42 | log.WithFields(logrus.Fields{ 43 | "temperature": -4, 44 | }).Debug("Temperature changes") 45 | 46 | log.WithFields(logrus.Fields{ 47 | "animal": "orca", 48 | "size": 9009, 49 | }).Panic("It's over 9000!") 50 | } 51 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | "github.com/Sirupsen/logrus/hooks/airbrake" 6 | ) 7 | 8 | var log = logrus.New() 9 | 10 | func init() { 11 | log.Formatter = new(logrus.TextFormatter) // default 12 | log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development")) 13 | } 14 | 15 | func main() { 16 | log.WithFields(logrus.Fields{ 17 | "animal": "walrus", 18 | "size": 10, 19 | }).Info("A group of walrus emerges from the ocean") 20 | 21 | log.WithFields(logrus.Fields{ 22 | "omg": true, 23 | "number": 122, 24 | }).Warn("The group's number increased tremendously!") 25 | 26 | log.WithFields(logrus.Fields{ 27 | "omg": true, 28 | "number": 100, 29 | }).Fatal("The ice breaks!") 30 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import "time" 4 | 5 | const DefaultTimestampFormat = time.RFC3339 6 | 7 | // The Formatter interface is used to implement a custom Formatter. It takes an 8 | // `Entry`. It exposes all the fields, including the default ones: 9 | // 10 | // * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. 11 | // * `entry.Data["time"]`. The timestamp. 12 | // * `entry.Data["level"]. The level the entry was logged at. 13 | // 14 | // Any additional fields added with `WithField` or `WithFields` are also in 15 | // `entry.Data`. Format is expected to return an array of bytes which are then 16 | // logged to `logger.Out`. 17 | type Formatter interface { 18 | Format(*Entry) ([]byte, error) 19 | } 20 | 21 | // This is to not silently overwrite `time`, `msg` and `level` fields when 22 | // dumping it. If this code wasn't there doing: 23 | // 24 | // logrus.WithField("level", 1).Info("hello") 25 | // 26 | // Would just silently drop the user provided level. Instead with this code 27 | // it'll logged as: 28 | // 29 | // {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} 30 | // 31 | // It's not exported because it's still using Data in an opinionated way. It's to 32 | // avoid code duplication between the two default formatters. 33 | func prefixFieldClashes(data Fields) { 34 | _, ok := data["time"] 35 | if ok { 36 | data["fields.time"] = data["time"] 37 | } 38 | 39 | _, ok = data["msg"] 40 | if ok { 41 | data["fields.msg"] = data["msg"] 42 | } 43 | 44 | _, ok = data["level"] 45 | if ok { 46 | data["fields.level"] = data["level"] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | // smallFields is a small size data set for benchmarking 9 | var smallFields = Fields{ 10 | "foo": "bar", 11 | "baz": "qux", 12 | "one": "two", 13 | "three": "four", 14 | } 15 | 16 | // largeFields is a large size data set for benchmarking 17 | var largeFields = Fields{ 18 | "foo": "bar", 19 | "baz": "qux", 20 | "one": "two", 21 | "three": "four", 22 | "five": "six", 23 | "seven": "eight", 24 | "nine": "ten", 25 | "eleven": "twelve", 26 | "thirteen": "fourteen", 27 | "fifteen": "sixteen", 28 | "seventeen": "eighteen", 29 | "nineteen": "twenty", 30 | "a": "b", 31 | "c": "d", 32 | "e": "f", 33 | "g": "h", 34 | "i": "j", 35 | "k": "l", 36 | "m": "n", 37 | "o": "p", 38 | "q": "r", 39 | "s": "t", 40 | "u": "v", 41 | "w": "x", 42 | "y": "z", 43 | "this": "will", 44 | "make": "thirty", 45 | "entries": "yeah", 46 | } 47 | 48 | func BenchmarkSmallTextFormatter(b *testing.B) { 49 | doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) 50 | } 51 | 52 | func BenchmarkLargeTextFormatter(b *testing.B) { 53 | doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) 54 | } 55 | 56 | func BenchmarkSmallColoredTextFormatter(b *testing.B) { 57 | doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) 58 | } 59 | 60 | func BenchmarkLargeColoredTextFormatter(b *testing.B) { 61 | doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) 62 | } 63 | 64 | func BenchmarkSmallJSONFormatter(b *testing.B) { 65 | doBenchmark(b, &JSONFormatter{}, smallFields) 66 | } 67 | 68 | func BenchmarkLargeJSONFormatter(b *testing.B) { 69 | doBenchmark(b, &JSONFormatter{}, largeFields) 70 | } 71 | 72 | func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { 73 | entry := &Entry{ 74 | Time: time.Time{}, 75 | Level: InfoLevel, 76 | Message: "message", 77 | Data: fields, 78 | } 79 | var d []byte 80 | var err error 81 | for i := 0; i < b.N; i++ { 82 | d, err = formatter.Format(entry) 83 | if err != nil { 84 | b.Fatal(err) 85 | } 86 | b.SetBytes(int64(len(d))) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go: -------------------------------------------------------------------------------- 1 | package logstash 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | ) 9 | 10 | // Formatter generates json in logstash format. 11 | // Logstash site: http://logstash.net/ 12 | type LogstashFormatter struct { 13 | Type string // if not empty use for logstash type field. 14 | 15 | // TimestampFormat sets the format used for timestamps. 16 | TimestampFormat string 17 | } 18 | 19 | func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { 20 | entry.Data["@version"] = 1 21 | 22 | if f.TimestampFormat == "" { 23 | f.TimestampFormat = logrus.DefaultTimestampFormat 24 | } 25 | 26 | entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) 27 | 28 | // set message field 29 | v, ok := entry.Data["message"] 30 | if ok { 31 | entry.Data["fields.message"] = v 32 | } 33 | entry.Data["message"] = entry.Message 34 | 35 | // set level field 36 | v, ok = entry.Data["level"] 37 | if ok { 38 | entry.Data["fields.level"] = v 39 | } 40 | entry.Data["level"] = entry.Level.String() 41 | 42 | // set type field 43 | if f.Type != "" { 44 | v, ok = entry.Data["type"] 45 | if ok { 46 | entry.Data["fields.type"] = v 47 | } 48 | entry.Data["type"] = f.Type 49 | } 50 | 51 | serialized, err := json.Marshal(entry.Data) 52 | if err != nil { 53 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 54 | } 55 | return append(serialized, '\n'), nil 56 | } 57 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go: -------------------------------------------------------------------------------- 1 | package logstash 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/Sirupsen/logrus" 7 | "github.com/stretchr/testify/assert" 8 | "testing" 9 | ) 10 | 11 | func TestLogstashFormatter(t *testing.T) { 12 | assert := assert.New(t) 13 | 14 | lf := LogstashFormatter{Type: "abc"} 15 | 16 | fields := logrus.Fields{ 17 | "message": "def", 18 | "level": "ijk", 19 | "type": "lmn", 20 | "one": 1, 21 | "pi": 3.14, 22 | "bool": true, 23 | } 24 | 25 | entry := logrus.WithFields(fields) 26 | entry.Message = "msg" 27 | entry.Level = logrus.InfoLevel 28 | 29 | b, _ := lf.Format(entry) 30 | 31 | var data map[string]interface{} 32 | dec := json.NewDecoder(bytes.NewReader(b)) 33 | dec.UseNumber() 34 | dec.Decode(&data) 35 | 36 | // base fields 37 | assert.Equal(json.Number("1"), data["@version"]) 38 | assert.NotEmpty(data["@timestamp"]) 39 | assert.Equal("abc", data["type"]) 40 | assert.Equal("msg", data["message"]) 41 | assert.Equal("info", data["level"]) 42 | 43 | // substituted fields 44 | assert.Equal("def", data["fields.message"]) 45 | assert.Equal("ijk", data["fields.level"]) 46 | assert.Equal("lmn", data["fields.type"]) 47 | 48 | // formats 49 | assert.Equal(json.Number("1"), data["one"]) 50 | assert.Equal(json.Number("3.14"), data["pi"]) 51 | assert.Equal(true, data["bool"]) 52 | } 53 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type TestHook struct { 10 | Fired bool 11 | } 12 | 13 | func (hook *TestHook) Fire(entry *Entry) error { 14 | hook.Fired = true 15 | return nil 16 | } 17 | 18 | func (hook *TestHook) Levels() []Level { 19 | return []Level{ 20 | DebugLevel, 21 | InfoLevel, 22 | WarnLevel, 23 | ErrorLevel, 24 | FatalLevel, 25 | PanicLevel, 26 | } 27 | } 28 | 29 | func TestHookFires(t *testing.T) { 30 | hook := new(TestHook) 31 | 32 | LogAndAssertJSON(t, func(log *Logger) { 33 | log.Hooks.Add(hook) 34 | assert.Equal(t, hook.Fired, false) 35 | 36 | log.Print("test") 37 | }, func(fields Fields) { 38 | assert.Equal(t, hook.Fired, true) 39 | }) 40 | } 41 | 42 | type ModifyHook struct { 43 | } 44 | 45 | func (hook *ModifyHook) Fire(entry *Entry) error { 46 | entry.Data["wow"] = "whale" 47 | return nil 48 | } 49 | 50 | func (hook *ModifyHook) Levels() []Level { 51 | return []Level{ 52 | DebugLevel, 53 | InfoLevel, 54 | WarnLevel, 55 | ErrorLevel, 56 | FatalLevel, 57 | PanicLevel, 58 | } 59 | } 60 | 61 | func TestHookCanModifyEntry(t *testing.T) { 62 | hook := new(ModifyHook) 63 | 64 | LogAndAssertJSON(t, func(log *Logger) { 65 | log.Hooks.Add(hook) 66 | log.WithField("wow", "elephant").Print("test") 67 | }, func(fields Fields) { 68 | assert.Equal(t, fields["wow"], "whale") 69 | }) 70 | } 71 | 72 | func TestCanFireMultipleHooks(t *testing.T) { 73 | hook1 := new(ModifyHook) 74 | hook2 := new(TestHook) 75 | 76 | LogAndAssertJSON(t, func(log *Logger) { 77 | log.Hooks.Add(hook1) 78 | log.Hooks.Add(hook2) 79 | 80 | log.WithField("wow", "elephant").Print("test") 81 | }, func(fields Fields) { 82 | assert.Equal(t, fields["wow"], "whale") 83 | assert.Equal(t, hook2.Fired, true) 84 | }) 85 | } 86 | 87 | type ErrorHook struct { 88 | Fired bool 89 | } 90 | 91 | func (hook *ErrorHook) Fire(entry *Entry) error { 92 | hook.Fired = true 93 | return nil 94 | } 95 | 96 | func (hook *ErrorHook) Levels() []Level { 97 | return []Level{ 98 | ErrorLevel, 99 | } 100 | } 101 | 102 | func TestErrorHookShouldntFireOnInfo(t *testing.T) { 103 | hook := new(ErrorHook) 104 | 105 | LogAndAssertJSON(t, func(log *Logger) { 106 | log.Hooks.Add(hook) 107 | log.Info("test") 108 | }, func(fields Fields) { 109 | assert.Equal(t, hook.Fired, false) 110 | }) 111 | } 112 | 113 | func TestErrorHookShouldFireOnError(t *testing.T) { 114 | hook := new(ErrorHook) 115 | 116 | LogAndAssertJSON(t, func(log *Logger) { 117 | log.Hooks.Add(hook) 118 | log.Error("test") 119 | }, func(fields Fields) { 120 | assert.Equal(t, hook.Fired, true) 121 | }) 122 | } 123 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | // A hook to be fired when logging on the logging levels returned from 4 | // `Levels()` on your implementation of the interface. Note that this is not 5 | // fired in a goroutine or a channel with workers, you should handle such 6 | // functionality yourself if your call is non-blocking and you don't wish for 7 | // the logging calls for levels returned from `Levels()` to block. 8 | type Hook interface { 9 | Levels() []Level 10 | Fire(*Entry) error 11 | } 12 | 13 | // Internal type for storing the hooks on a logger instance. 14 | type levelHooks map[Level][]Hook 15 | 16 | // Add a hook to an instance of logger. This is called with 17 | // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. 18 | func (hooks levelHooks) Add(hook Hook) { 19 | for _, level := range hook.Levels() { 20 | hooks[level] = append(hooks[level], hook) 21 | } 22 | } 23 | 24 | // Fire all the hooks for the passed level. Used by `entry.log` to fire 25 | // appropriate hooks for a log entry. 26 | func (hooks levelHooks) Fire(level Level, entry *Entry) error { 27 | for _, hook := range hooks[level] { 28 | if err := hook.Fire(entry); err != nil { 29 | return err 30 | } 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go: -------------------------------------------------------------------------------- 1 | package airbrake 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/tobi/airbrake-go" 9 | ) 10 | 11 | // AirbrakeHook to send exceptions to an exception-tracking service compatible 12 | // with the Airbrake API. 13 | type airbrakeHook struct { 14 | APIKey string 15 | Endpoint string 16 | Environment string 17 | } 18 | 19 | func NewHook(endpoint, apiKey, env string) *airbrakeHook { 20 | return &airbrakeHook{ 21 | APIKey: apiKey, 22 | Endpoint: endpoint, 23 | Environment: env, 24 | } 25 | } 26 | 27 | func (hook *airbrakeHook) Fire(entry *logrus.Entry) error { 28 | airbrake.ApiKey = hook.APIKey 29 | airbrake.Endpoint = hook.Endpoint 30 | airbrake.Environment = hook.Environment 31 | 32 | var notifyErr error 33 | err, ok := entry.Data["error"].(error) 34 | if ok { 35 | notifyErr = err 36 | } else { 37 | notifyErr = errors.New(entry.Message) 38 | } 39 | 40 | airErr := airbrake.Notify(notifyErr) 41 | if airErr != nil { 42 | return fmt.Errorf("Failed to send error to Airbrake: %s", airErr) 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func (hook *airbrakeHook) Levels() []logrus.Level { 49 | return []logrus.Level{ 50 | logrus.ErrorLevel, 51 | logrus.FatalLevel, 52 | logrus.PanicLevel, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake_test.go: -------------------------------------------------------------------------------- 1 | package airbrake 2 | 3 | import ( 4 | "encoding/xml" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | "time" 9 | 10 | "github.com/Sirupsen/logrus" 11 | ) 12 | 13 | type notice struct { 14 | Error NoticeError `xml:"error"` 15 | } 16 | type NoticeError struct { 17 | Class string `xml:"class"` 18 | Message string `xml:"message"` 19 | } 20 | 21 | type customErr struct { 22 | msg string 23 | } 24 | 25 | func (e *customErr) Error() string { 26 | return e.msg 27 | } 28 | 29 | const ( 30 | testAPIKey = "abcxyz" 31 | testEnv = "development" 32 | expectedClass = "*airbrake.customErr" 33 | expectedMsg = "foo" 34 | unintendedMsg = "Airbrake will not see this string" 35 | ) 36 | 37 | var ( 38 | noticeError = make(chan NoticeError, 1) 39 | ) 40 | 41 | // TestLogEntryMessageReceived checks if invoking Logrus' log.Error 42 | // method causes an XML payload containing the log entry message is received 43 | // by a HTTP server emulating an Airbrake-compatible endpoint. 44 | func TestLogEntryMessageReceived(t *testing.T) { 45 | log := logrus.New() 46 | ts := startAirbrakeServer(t) 47 | defer ts.Close() 48 | 49 | hook := NewHook(ts.URL, testAPIKey, "production") 50 | log.Hooks.Add(hook) 51 | 52 | log.Error(expectedMsg) 53 | 54 | select { 55 | case received := <-noticeError: 56 | if received.Message != expectedMsg { 57 | t.Errorf("Unexpected message received: %s", received.Message) 58 | } 59 | case <-time.After(time.Second): 60 | t.Error("Timed out; no notice received by Airbrake API") 61 | } 62 | } 63 | 64 | // TestLogEntryMessageReceived confirms that, when passing an error type using 65 | // logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the 66 | // error message returned by the Error() method on the error interface 67 | // rather than the logrus.Entry.Message string. 68 | func TestLogEntryWithErrorReceived(t *testing.T) { 69 | log := logrus.New() 70 | ts := startAirbrakeServer(t) 71 | defer ts.Close() 72 | 73 | hook := NewHook(ts.URL, testAPIKey, "production") 74 | log.Hooks.Add(hook) 75 | 76 | log.WithFields(logrus.Fields{ 77 | "error": &customErr{expectedMsg}, 78 | }).Error(unintendedMsg) 79 | 80 | select { 81 | case received := <-noticeError: 82 | if received.Message != expectedMsg { 83 | t.Errorf("Unexpected message received: %s", received.Message) 84 | } 85 | if received.Class != expectedClass { 86 | t.Errorf("Unexpected error class: %s", received.Class) 87 | } 88 | case <-time.After(time.Second): 89 | t.Error("Timed out; no notice received by Airbrake API") 90 | } 91 | } 92 | 93 | // TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a 94 | // non-error type using logrus.Fields, a HTTP server emulating an Airbrake 95 | // endpoint receives the logrus.Entry.Message string. 96 | // 97 | // Only error types are supported when setting the 'error' field using 98 | // logrus.WithFields(). 99 | func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) { 100 | log := logrus.New() 101 | ts := startAirbrakeServer(t) 102 | defer ts.Close() 103 | 104 | hook := NewHook(ts.URL, testAPIKey, "production") 105 | log.Hooks.Add(hook) 106 | 107 | log.WithFields(logrus.Fields{ 108 | "error": expectedMsg, 109 | }).Error(unintendedMsg) 110 | 111 | select { 112 | case received := <-noticeError: 113 | if received.Message != unintendedMsg { 114 | t.Errorf("Unexpected message received: %s", received.Message) 115 | } 116 | case <-time.After(time.Second): 117 | t.Error("Timed out; no notice received by Airbrake API") 118 | } 119 | } 120 | 121 | func startAirbrakeServer(t *testing.T) *httptest.Server { 122 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 123 | var notice notice 124 | if err := xml.NewDecoder(r.Body).Decode(¬ice); err != nil { 125 | t.Error(err) 126 | } 127 | r.Body.Close() 128 | 129 | noticeError <- notice.Error 130 | })) 131 | 132 | return ts 133 | } 134 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go: -------------------------------------------------------------------------------- 1 | package logrus_bugsnag 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/Sirupsen/logrus" 7 | "github.com/bugsnag/bugsnag-go" 8 | ) 9 | 10 | type bugsnagHook struct{} 11 | 12 | // ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before 13 | // bugsnag.Configure. Bugsnag must be configured before the hook. 14 | var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook") 15 | 16 | // ErrBugsnagSendFailed indicates that the hook failed to submit an error to 17 | // bugsnag. The error was successfully generated, but `bugsnag.Notify()` 18 | // failed. 19 | type ErrBugsnagSendFailed struct { 20 | err error 21 | } 22 | 23 | func (e ErrBugsnagSendFailed) Error() string { 24 | return "failed to send error to Bugsnag: " + e.err.Error() 25 | } 26 | 27 | // NewBugsnagHook initializes a logrus hook which sends exceptions to an 28 | // exception-tracking service compatible with the Bugsnag API. Before using 29 | // this hook, you must call bugsnag.Configure(). The returned object should be 30 | // registered with a log via `AddHook()` 31 | // 32 | // Entries that trigger an Error, Fatal or Panic should now include an "error" 33 | // field to send to Bugsnag. 34 | func NewBugsnagHook() (*bugsnagHook, error) { 35 | if bugsnag.Config.APIKey == "" { 36 | return nil, ErrBugsnagUnconfigured 37 | } 38 | return &bugsnagHook{}, nil 39 | } 40 | 41 | // Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the 42 | // "error" field (or the Message if the error isn't present) and sends it off. 43 | func (hook *bugsnagHook) Fire(entry *logrus.Entry) error { 44 | var notifyErr error 45 | err, ok := entry.Data["error"].(error) 46 | if ok { 47 | notifyErr = err 48 | } else { 49 | notifyErr = errors.New(entry.Message) 50 | } 51 | 52 | bugsnagErr := bugsnag.Notify(notifyErr) 53 | if bugsnagErr != nil { 54 | return ErrBugsnagSendFailed{bugsnagErr} 55 | } 56 | 57 | return nil 58 | } 59 | 60 | // Levels enumerates the log levels on which the error should be forwarded to 61 | // bugsnag: everything at or above the "Error" level. 62 | func (hook *bugsnagHook) Levels() []logrus.Level { 63 | return []logrus.Level{ 64 | logrus.ErrorLevel, 65 | logrus.FatalLevel, 66 | logrus.PanicLevel, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go: -------------------------------------------------------------------------------- 1 | package logrus_bugsnag 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | "time" 11 | 12 | "github.com/Sirupsen/logrus" 13 | "github.com/bugsnag/bugsnag-go" 14 | ) 15 | 16 | type notice struct { 17 | Events []struct { 18 | Exceptions []struct { 19 | Message string `json:"message"` 20 | } `json:"exceptions"` 21 | } `json:"events"` 22 | } 23 | 24 | func TestNoticeReceived(t *testing.T) { 25 | msg := make(chan string, 1) 26 | expectedMsg := "foo" 27 | 28 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 29 | var notice notice 30 | data, _ := ioutil.ReadAll(r.Body) 31 | if err := json.Unmarshal(data, ¬ice); err != nil { 32 | t.Error(err) 33 | } 34 | _ = r.Body.Close() 35 | 36 | msg <- notice.Events[0].Exceptions[0].Message 37 | })) 38 | defer ts.Close() 39 | 40 | hook := &bugsnagHook{} 41 | 42 | bugsnag.Configure(bugsnag.Configuration{ 43 | Endpoint: ts.URL, 44 | ReleaseStage: "production", 45 | APIKey: "12345678901234567890123456789012", 46 | Synchronous: true, 47 | }) 48 | 49 | log := logrus.New() 50 | log.Hooks.Add(hook) 51 | 52 | log.WithFields(logrus.Fields{ 53 | "error": errors.New(expectedMsg), 54 | }).Error("Bugsnag will not see this string") 55 | 56 | select { 57 | case received := <-msg: 58 | if received != expectedMsg { 59 | t.Errorf("Unexpected message received: %s", received) 60 | } 61 | case <-time.After(time.Second): 62 | t.Error("Timed out; no notice received by Bugsnag API") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md: -------------------------------------------------------------------------------- 1 | # Papertrail Hook for Logrus :walrus: 2 | 3 | [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). 4 | 5 | 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. 6 | 7 | ## Usage 8 | 9 | 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`. 10 | 11 | For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs. 12 | 13 | ```go 14 | import ( 15 | "log/syslog" 16 | "github.com/Sirupsen/logrus" 17 | "github.com/Sirupsen/logrus/hooks/papertrail" 18 | ) 19 | 20 | func main() { 21 | log := logrus.New() 22 | hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME) 23 | 24 | if err == nil { 25 | log.Hooks.Add(hook) 26 | } 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go: -------------------------------------------------------------------------------- 1 | package logrus_papertrail 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "time" 8 | 9 | "github.com/Sirupsen/logrus" 10 | ) 11 | 12 | const ( 13 | format = "Jan 2 15:04:05" 14 | ) 15 | 16 | // PapertrailHook to send logs to a logging service compatible with the Papertrail API. 17 | type PapertrailHook struct { 18 | Host string 19 | Port int 20 | AppName string 21 | UDPConn net.Conn 22 | } 23 | 24 | // NewPapertrailHook creates a hook to be added to an instance of logger. 25 | func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) { 26 | conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port)) 27 | return &PapertrailHook{host, port, appName, conn}, err 28 | } 29 | 30 | // Fire is called when a log event is fired. 31 | func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { 32 | date := time.Now().Format(format) 33 | msg, _ := entry.String() 34 | payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg) 35 | 36 | bytesWritten, err := hook.UDPConn.Write([]byte(payload)) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | 45 | // Levels returns the available logging levels. 46 | func (hook *PapertrailHook) Levels() []logrus.Level { 47 | return []logrus.Level{ 48 | logrus.PanicLevel, 49 | logrus.FatalLevel, 50 | logrus.ErrorLevel, 51 | logrus.WarnLevel, 52 | logrus.InfoLevel, 53 | logrus.DebugLevel, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go: -------------------------------------------------------------------------------- 1 | package logrus_papertrail 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/stvp/go-udp-testing" 9 | ) 10 | 11 | func TestWritingToUDP(t *testing.T) { 12 | port := 16661 13 | udp.SetAddr(fmt.Sprintf(":%d", port)) 14 | 15 | hook, err := NewPapertrailHook("localhost", port, "test") 16 | if err != nil { 17 | t.Errorf("Unable to connect to local UDP server.") 18 | } 19 | 20 | log := logrus.New() 21 | log.Hooks.Add(hook) 22 | 23 | udp.ShouldReceive(t, "foo", func() { 24 | log.Info("foo") 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md: -------------------------------------------------------------------------------- 1 | # Sentry Hook for Logrus :walrus: 2 | 3 | [Sentry](https://getsentry.com) provides both self-hosted and hosted 4 | solutions for exception tracking. 5 | Both client and server are 6 | [open source](https://github.com/getsentry/sentry). 7 | 8 | ## Usage 9 | 10 | Every sentry application defined on the server gets a different 11 | [DSN](https://www.getsentry.com/docs/). In the example below replace 12 | `YOUR_DSN` with the one created for your application. 13 | 14 | ```go 15 | import ( 16 | "github.com/Sirupsen/logrus" 17 | "github.com/Sirupsen/logrus/hooks/sentry" 18 | ) 19 | 20 | func main() { 21 | log := logrus.New() 22 | hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{ 23 | logrus.PanicLevel, 24 | logrus.FatalLevel, 25 | logrus.ErrorLevel, 26 | }) 27 | 28 | if err == nil { 29 | log.Hooks.Add(hook) 30 | } 31 | } 32 | ``` 33 | 34 | ## Special fields 35 | 36 | Some logrus fields have a special meaning in this hook, 37 | these are `server_name`, `logger` and `http_request`. 38 | When logs are sent to sentry these fields are treated differently. 39 | - `server_name` (also known as hostname) is the name of the server which 40 | is logging the event (hostname.example.com) 41 | - `logger` is the part of the application which is logging the event. 42 | In go this usually means setting it to the name of the package. 43 | - `http_request` is the in-coming request(*http.Request). The detailed request data are sent to Sentry. 44 | 45 | ## Timeout 46 | 47 | `Timeout` is the time the sentry hook will wait for a response 48 | from the sentry server. 49 | 50 | If this time elapses with no response from 51 | the server an error will be returned. 52 | 53 | If `Timeout` is set to 0 the SentryHook will not wait for a reply 54 | and will assume a correct delivery. 55 | 56 | The SentryHook has a default timeout of `100 milliseconds` when created 57 | with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field: 58 | 59 | ```go 60 | hook, _ := logrus_sentry.NewSentryHook(...) 61 | hook.Timeout = 20*time.Second 62 | ``` 63 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go: -------------------------------------------------------------------------------- 1 | package logrus_sentry 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "net/http" 7 | 8 | "github.com/Sirupsen/logrus" 9 | "github.com/getsentry/raven-go" 10 | ) 11 | 12 | var ( 13 | severityMap = map[logrus.Level]raven.Severity{ 14 | logrus.DebugLevel: raven.DEBUG, 15 | logrus.InfoLevel: raven.INFO, 16 | logrus.WarnLevel: raven.WARNING, 17 | logrus.ErrorLevel: raven.ERROR, 18 | logrus.FatalLevel: raven.FATAL, 19 | logrus.PanicLevel: raven.FATAL, 20 | } 21 | ) 22 | 23 | func getAndDel(d logrus.Fields, key string) (string, bool) { 24 | var ( 25 | ok bool 26 | v interface{} 27 | val string 28 | ) 29 | if v, ok = d[key]; !ok { 30 | return "", false 31 | } 32 | 33 | if val, ok = v.(string); !ok { 34 | return "", false 35 | } 36 | delete(d, key) 37 | return val, true 38 | } 39 | 40 | func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) { 41 | var ( 42 | ok bool 43 | v interface{} 44 | req *http.Request 45 | ) 46 | if v, ok = d[key]; !ok { 47 | return nil, false 48 | } 49 | if req, ok = v.(*http.Request); !ok || req == nil { 50 | return nil, false 51 | } 52 | delete(d, key) 53 | return req, true 54 | } 55 | 56 | // SentryHook delivers logs to a sentry server. 57 | type SentryHook struct { 58 | // Timeout sets the time to wait for a delivery error from the sentry server. 59 | // If this is set to zero the server will not wait for any response and will 60 | // consider the message correctly sent 61 | Timeout time.Duration 62 | 63 | client *raven.Client 64 | levels []logrus.Level 65 | } 66 | 67 | // NewSentryHook creates a hook to be added to an instance of logger 68 | // and initializes the raven client. 69 | // This method sets the timeout to 100 milliseconds. 70 | func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) { 71 | client, err := raven.NewClient(DSN, nil) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return &SentryHook{100 * time.Millisecond, client, levels}, nil 76 | } 77 | 78 | // Called when an event should be sent to sentry 79 | // Special fields that sentry uses to give more information to the server 80 | // are extracted from entry.Data (if they are found) 81 | // These fields are: logger, server_name and http_request 82 | func (hook *SentryHook) Fire(entry *logrus.Entry) error { 83 | packet := &raven.Packet{ 84 | Message: entry.Message, 85 | Timestamp: raven.Timestamp(entry.Time), 86 | Level: severityMap[entry.Level], 87 | Platform: "go", 88 | } 89 | 90 | d := entry.Data 91 | 92 | if logger, ok := getAndDel(d, "logger"); ok { 93 | packet.Logger = logger 94 | } 95 | if serverName, ok := getAndDel(d, "server_name"); ok { 96 | packet.ServerName = serverName 97 | } 98 | if req, ok := getAndDelRequest(d, "http_request"); ok { 99 | packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req)) 100 | } 101 | packet.Extra = map[string]interface{}(d) 102 | 103 | _, errCh := hook.client.Capture(packet, nil) 104 | timeout := hook.Timeout 105 | if timeout != 0 { 106 | timeoutCh := time.After(timeout) 107 | select { 108 | case err := <-errCh: 109 | return err 110 | case <-timeoutCh: 111 | return fmt.Errorf("no response from sentry server in %s", timeout) 112 | } 113 | } 114 | return nil 115 | } 116 | 117 | // Levels returns the available logging levels. 118 | func (hook *SentryHook) Levels() []logrus.Level { 119 | return hook.levels 120 | } 121 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry_test.go: -------------------------------------------------------------------------------- 1 | package logrus_sentry 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/Sirupsen/logrus" 13 | "github.com/getsentry/raven-go" 14 | ) 15 | 16 | const ( 17 | message = "error message" 18 | server_name = "testserver.internal" 19 | logger_name = "test.logger" 20 | ) 21 | 22 | func getTestLogger() *logrus.Logger { 23 | l := logrus.New() 24 | l.Out = ioutil.Discard 25 | return l 26 | } 27 | 28 | func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) { 29 | pch := make(chan *raven.Packet, 1) 30 | s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 31 | defer req.Body.Close() 32 | d := json.NewDecoder(req.Body) 33 | p := &raven.Packet{} 34 | err := d.Decode(p) 35 | if err != nil { 36 | t.Fatal(err.Error()) 37 | } 38 | 39 | pch <- p 40 | })) 41 | defer s.Close() 42 | 43 | fragments := strings.SplitN(s.URL, "://", 2) 44 | dsn := fmt.Sprintf( 45 | "%s://public:secret@%s/sentry/project-id", 46 | fragments[0], 47 | fragments[1], 48 | ) 49 | tf(dsn, pch) 50 | } 51 | 52 | func TestSpecialFields(t *testing.T) { 53 | WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { 54 | logger := getTestLogger() 55 | 56 | hook, err := NewSentryHook(dsn, []logrus.Level{ 57 | logrus.ErrorLevel, 58 | }) 59 | 60 | if err != nil { 61 | t.Fatal(err.Error()) 62 | } 63 | logger.Hooks.Add(hook) 64 | 65 | req, _ := http.NewRequest("GET", "url", nil) 66 | logger.WithFields(logrus.Fields{ 67 | "server_name": server_name, 68 | "logger": logger_name, 69 | "http_request": req, 70 | }).Error(message) 71 | 72 | packet := <-pch 73 | if packet.Logger != logger_name { 74 | t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger) 75 | } 76 | 77 | if packet.ServerName != server_name { 78 | t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName) 79 | } 80 | }) 81 | } 82 | 83 | func TestSentryHandler(t *testing.T) { 84 | WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { 85 | logger := getTestLogger() 86 | hook, err := NewSentryHook(dsn, []logrus.Level{ 87 | logrus.ErrorLevel, 88 | }) 89 | if err != nil { 90 | t.Fatal(err.Error()) 91 | } 92 | logger.Hooks.Add(hook) 93 | 94 | logger.Error(message) 95 | packet := <-pch 96 | if packet.Message != message { 97 | t.Errorf("message should have been %s, was %s", message, packet.Message) 98 | } 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md: -------------------------------------------------------------------------------- 1 | # Syslog Hooks for Logrus :walrus: 2 | 3 | ## Usage 4 | 5 | ```go 6 | import ( 7 | "log/syslog" 8 | "github.com/Sirupsen/logrus" 9 | logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" 10 | ) 11 | 12 | func main() { 13 | log := logrus.New() 14 | hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") 15 | 16 | if err == nil { 17 | log.Hooks.Add(hook) 18 | } 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go: -------------------------------------------------------------------------------- 1 | package logrus_syslog 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Sirupsen/logrus" 6 | "log/syslog" 7 | "os" 8 | ) 9 | 10 | // SyslogHook to send logs via syslog. 11 | type SyslogHook struct { 12 | Writer *syslog.Writer 13 | SyslogNetwork string 14 | SyslogRaddr string 15 | } 16 | 17 | // Creates a hook to be added to an instance of logger. This is called with 18 | // `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")` 19 | // `if err == nil { log.Hooks.Add(hook) }` 20 | func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) { 21 | w, err := syslog.Dial(network, raddr, priority, tag) 22 | return &SyslogHook{w, network, raddr}, err 23 | } 24 | 25 | func (hook *SyslogHook) Fire(entry *logrus.Entry) error { 26 | line, err := entry.String() 27 | if err != nil { 28 | fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) 29 | return err 30 | } 31 | 32 | switch entry.Level { 33 | case logrus.PanicLevel: 34 | return hook.Writer.Crit(line) 35 | case logrus.FatalLevel: 36 | return hook.Writer.Crit(line) 37 | case logrus.ErrorLevel: 38 | return hook.Writer.Err(line) 39 | case logrus.WarnLevel: 40 | return hook.Writer.Warning(line) 41 | case logrus.InfoLevel: 42 | return hook.Writer.Info(line) 43 | case logrus.DebugLevel: 44 | return hook.Writer.Debug(line) 45 | default: 46 | return nil 47 | } 48 | } 49 | 50 | func (hook *SyslogHook) Levels() []logrus.Level { 51 | return []logrus.Level{ 52 | logrus.PanicLevel, 53 | logrus.FatalLevel, 54 | logrus.ErrorLevel, 55 | logrus.WarnLevel, 56 | logrus.InfoLevel, 57 | logrus.DebugLevel, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go: -------------------------------------------------------------------------------- 1 | package logrus_syslog 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | "log/syslog" 6 | "testing" 7 | ) 8 | 9 | func TestLocalhostAddAndPrint(t *testing.T) { 10 | log := logrus.New() 11 | hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") 12 | 13 | if err != nil { 14 | t.Errorf("Unable to connect to local syslog.") 15 | } 16 | 17 | log.Hooks.Add(hook) 18 | 19 | for _, level := range hook.Levels() { 20 | if len(log.Hooks[level]) != 1 { 21 | t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level])) 22 | } 23 | } 24 | 25 | log.Info("Congratulations!") 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type JSONFormatter struct { 9 | // TimestampFormat sets the format used for marshaling timestamps. 10 | TimestampFormat string 11 | } 12 | 13 | func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { 14 | data := make(Fields, len(entry.Data)+3) 15 | for k, v := range entry.Data { 16 | switch v := v.(type) { 17 | case error: 18 | // Otherwise errors are ignored by `encoding/json` 19 | // https://github.com/Sirupsen/logrus/issues/137 20 | data[k] = v.Error() 21 | default: 22 | data[k] = v 23 | } 24 | } 25 | prefixFieldClashes(data) 26 | 27 | timestampFormat := f.TimestampFormat 28 | if timestampFormat == "" { 29 | timestampFormat = DefaultTimestampFormat 30 | } 31 | 32 | data["time"] = entry.Time.Format(timestampFormat) 33 | data["msg"] = entry.Message 34 | data["level"] = entry.Level.String() 35 | 36 | serialized, err := json.Marshal(data) 37 | if err != nil { 38 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 39 | } 40 | return append(serialized, '\n'), nil 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestErrorNotLost(t *testing.T) { 11 | formatter := &JSONFormatter{} 12 | 13 | b, err := formatter.Format(WithField("error", errors.New("wild walrus"))) 14 | if err != nil { 15 | t.Fatal("Unable to format entry: ", err) 16 | } 17 | 18 | entry := make(map[string]interface{}) 19 | err = json.Unmarshal(b, &entry) 20 | if err != nil { 21 | t.Fatal("Unable to unmarshal formatted entry: ", err) 22 | } 23 | 24 | if entry["error"] != "wild walrus" { 25 | t.Fatal("Error field not set") 26 | } 27 | } 28 | 29 | func TestErrorNotLostOnFieldNotNamedError(t *testing.T) { 30 | formatter := &JSONFormatter{} 31 | 32 | b, err := formatter.Format(WithField("omg", errors.New("wild walrus"))) 33 | if err != nil { 34 | t.Fatal("Unable to format entry: ", err) 35 | } 36 | 37 | entry := make(map[string]interface{}) 38 | err = json.Unmarshal(b, &entry) 39 | if err != nil { 40 | t.Fatal("Unable to unmarshal formatted entry: ", err) 41 | } 42 | 43 | if entry["omg"] != "wild walrus" { 44 | t.Fatal("Error field not set") 45 | } 46 | } 47 | 48 | func TestFieldClashWithTime(t *testing.T) { 49 | formatter := &JSONFormatter{} 50 | 51 | b, err := formatter.Format(WithField("time", "right now!")) 52 | if err != nil { 53 | t.Fatal("Unable to format entry: ", err) 54 | } 55 | 56 | entry := make(map[string]interface{}) 57 | err = json.Unmarshal(b, &entry) 58 | if err != nil { 59 | t.Fatal("Unable to unmarshal formatted entry: ", err) 60 | } 61 | 62 | if entry["fields.time"] != "right now!" { 63 | t.Fatal("fields.time not set to original time field") 64 | } 65 | 66 | if entry["time"] != "0001-01-01T00:00:00Z" { 67 | t.Fatal("time field not set to current time, was: ", entry["time"]) 68 | } 69 | } 70 | 71 | func TestFieldClashWithMsg(t *testing.T) { 72 | formatter := &JSONFormatter{} 73 | 74 | b, err := formatter.Format(WithField("msg", "something")) 75 | if err != nil { 76 | t.Fatal("Unable to format entry: ", err) 77 | } 78 | 79 | entry := make(map[string]interface{}) 80 | err = json.Unmarshal(b, &entry) 81 | if err != nil { 82 | t.Fatal("Unable to unmarshal formatted entry: ", err) 83 | } 84 | 85 | if entry["fields.msg"] != "something" { 86 | t.Fatal("fields.msg not set to original msg field") 87 | } 88 | } 89 | 90 | func TestFieldClashWithLevel(t *testing.T) { 91 | formatter := &JSONFormatter{} 92 | 93 | b, err := formatter.Format(WithField("level", "something")) 94 | if err != nil { 95 | t.Fatal("Unable to format entry: ", err) 96 | } 97 | 98 | entry := make(map[string]interface{}) 99 | err = json.Unmarshal(b, &entry) 100 | if err != nil { 101 | t.Fatal("Unable to unmarshal formatted entry: ", err) 102 | } 103 | 104 | if entry["fields.level"] != "something" { 105 | t.Fatal("fields.level not set to original level field") 106 | } 107 | } 108 | 109 | func TestJSONEntryEndsWithNewline(t *testing.T) { 110 | formatter := &JSONFormatter{} 111 | 112 | b, err := formatter.Format(WithField("level", "something")) 113 | if err != nil { 114 | t.Fatal("Unable to format entry: ", err) 115 | } 116 | 117 | if b[len(b)-1] != '\n' { 118 | t.Fatal("Expected JSON log entry to end with a newline") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | // Fields type, used to pass to `WithFields`. 9 | type Fields map[string]interface{} 10 | 11 | // Level type 12 | type Level uint8 13 | 14 | // Convert the Level to a string. E.g. PanicLevel becomes "panic". 15 | func (level Level) String() string { 16 | switch level { 17 | case DebugLevel: 18 | return "debug" 19 | case InfoLevel: 20 | return "info" 21 | case WarnLevel: 22 | return "warning" 23 | case ErrorLevel: 24 | return "error" 25 | case FatalLevel: 26 | return "fatal" 27 | case PanicLevel: 28 | return "panic" 29 | } 30 | 31 | return "unknown" 32 | } 33 | 34 | // ParseLevel takes a string level and returns the Logrus log level constant. 35 | func ParseLevel(lvl string) (Level, error) { 36 | switch lvl { 37 | case "panic": 38 | return PanicLevel, nil 39 | case "fatal": 40 | return FatalLevel, nil 41 | case "error": 42 | return ErrorLevel, nil 43 | case "warn", "warning": 44 | return WarnLevel, nil 45 | case "info": 46 | return InfoLevel, nil 47 | case "debug": 48 | return DebugLevel, nil 49 | } 50 | 51 | var l Level 52 | return l, fmt.Errorf("not a valid logrus Level: %q", lvl) 53 | } 54 | 55 | // These are the different logging levels. You can set the logging level to log 56 | // on your instance of logger, obtained with `logrus.New()`. 57 | const ( 58 | // PanicLevel level, highest level of severity. Logs and then calls panic with the 59 | // message passed to Debug, Info, ... 60 | PanicLevel Level = iota 61 | // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the 62 | // logging level is set to Panic. 63 | FatalLevel 64 | // ErrorLevel level. Logs. Used for errors that should definitely be noted. 65 | // Commonly used for hooks to send errors to an error tracking service. 66 | ErrorLevel 67 | // WarnLevel level. Non-critical entries that deserve eyes. 68 | WarnLevel 69 | // InfoLevel level. General operational entries about what's going on inside the 70 | // application. 71 | InfoLevel 72 | // DebugLevel level. Usually only enabled when debugging. Very verbose logging. 73 | DebugLevel 74 | ) 75 | 76 | // Won't compile if StdLogger can't be realized by a log.Logger 77 | var _ StdLogger = &log.Logger{} 78 | 79 | // StdLogger is what your logrus-enabled library should take, that way 80 | // it'll accept a stdlib logger and a logrus logger. There's no standard 81 | // interface, this is the closest we get, unfortunately. 82 | type StdLogger interface { 83 | Print(...interface{}) 84 | Printf(string, ...interface{}) 85 | Println(...interface{}) 86 | 87 | Fatal(...interface{}) 88 | Fatalf(string, ...interface{}) 89 | Fatalln(...interface{}) 90 | 91 | Panic(...interface{}) 92 | Panicf(string, ...interface{}) 93 | Panicln(...interface{}) 94 | } 95 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2013 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package logrus 7 | 8 | import "syscall" 9 | 10 | const ioctlReadTermios = syscall.TIOCGETA 11 | 12 | type Termios syscall.Termios 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. 3 | */ 4 | package logrus 5 | 6 | import ( 7 | "syscall" 8 | ) 9 | 10 | const ioctlReadTermios = syscall.TIOCGETA 11 | 12 | type Termios struct { 13 | Iflag uint32 14 | Oflag uint32 15 | Cflag uint32 16 | Lflag uint32 17 | Cc [20]uint8 18 | Ispeed uint32 19 | Ospeed uint32 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2013 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package logrus 7 | 8 | import "syscall" 9 | 10 | const ioctlReadTermios = syscall.TCGETS 11 | 12 | type Termios syscall.Termios 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build linux darwin freebsd openbsd 7 | 8 | package logrus 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | // IsTerminal returns true if the given file descriptor is a terminal. 16 | func IsTerminal() bool { 17 | fd := syscall.Stdout 18 | var termios Termios 19 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 20 | return err == 0 21 | } 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import "syscall" 4 | 5 | const ioctlReadTermios = syscall.TIOCGETA 6 | 7 | type Termios syscall.Termios 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build windows 7 | 8 | package logrus 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") 16 | 17 | var ( 18 | procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 19 | ) 20 | 21 | // IsTerminal returns true if the given file descriptor is a terminal. 22 | func IsTerminal() bool { 23 | fd := syscall.Stdout 24 | var st uint32 25 | r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 26 | return r != 0 && e == 0 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "runtime" 7 | "sort" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | const ( 13 | nocolor = 0 14 | red = 31 15 | green = 32 16 | yellow = 33 17 | blue = 34 18 | gray = 37 19 | ) 20 | 21 | var ( 22 | baseTimestamp time.Time 23 | isTerminal bool 24 | ) 25 | 26 | func init() { 27 | baseTimestamp = time.Now() 28 | isTerminal = IsTerminal() 29 | } 30 | 31 | func miniTS() int { 32 | return int(time.Since(baseTimestamp) / time.Second) 33 | } 34 | 35 | type TextFormatter struct { 36 | // Set to true to bypass checking for a TTY before outputting colors. 37 | ForceColors bool 38 | 39 | // Force disabling colors. 40 | DisableColors bool 41 | 42 | // Disable timestamp logging. useful when output is redirected to logging 43 | // system that already adds timestamps. 44 | DisableTimestamp bool 45 | 46 | // Enable logging the full timestamp when a TTY is attached instead of just 47 | // the time passed since beginning of execution. 48 | FullTimestamp bool 49 | 50 | // TimestampFormat to use for display when a full timestamp is printed 51 | TimestampFormat string 52 | 53 | // The fields are sorted by default for a consistent output. For applications 54 | // that log extremely frequently and don't use the JSON formatter this may not 55 | // be desired. 56 | DisableSorting bool 57 | } 58 | 59 | func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { 60 | var keys []string = make([]string, 0, len(entry.Data)) 61 | for k := range entry.Data { 62 | keys = append(keys, k) 63 | } 64 | 65 | if !f.DisableSorting { 66 | sort.Strings(keys) 67 | } 68 | 69 | b := &bytes.Buffer{} 70 | 71 | prefixFieldClashes(entry.Data) 72 | 73 | isColorTerminal := isTerminal && (runtime.GOOS != "windows") 74 | isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors 75 | 76 | if f.TimestampFormat == "" { 77 | f.TimestampFormat = DefaultTimestampFormat 78 | } 79 | if isColored { 80 | f.printColored(b, entry, keys) 81 | } else { 82 | if !f.DisableTimestamp { 83 | f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat)) 84 | } 85 | f.appendKeyValue(b, "level", entry.Level.String()) 86 | f.appendKeyValue(b, "msg", entry.Message) 87 | for _, key := range keys { 88 | f.appendKeyValue(b, key, entry.Data[key]) 89 | } 90 | } 91 | 92 | b.WriteByte('\n') 93 | return b.Bytes(), nil 94 | } 95 | 96 | func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) { 97 | var levelColor int 98 | switch entry.Level { 99 | case DebugLevel: 100 | levelColor = gray 101 | case WarnLevel: 102 | levelColor = yellow 103 | case ErrorLevel, FatalLevel, PanicLevel: 104 | levelColor = red 105 | default: 106 | levelColor = blue 107 | } 108 | 109 | levelText := strings.ToUpper(entry.Level.String())[0:4] 110 | 111 | if !f.FullTimestamp { 112 | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) 113 | } else { 114 | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message) 115 | } 116 | for _, k := range keys { 117 | v := entry.Data[k] 118 | fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) 119 | } 120 | } 121 | 122 | func needsQuoting(text string) bool { 123 | for _, ch := range text { 124 | if !((ch >= 'a' && ch <= 'z') || 125 | (ch >= 'A' && ch <= 'Z') || 126 | (ch >= '0' && ch <= '9') || 127 | ch == '-' || ch == '.') { 128 | return false 129 | } 130 | } 131 | return true 132 | } 133 | 134 | func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { 135 | switch value.(type) { 136 | case string: 137 | if needsQuoting(value.(string)) { 138 | fmt.Fprintf(b, "%v=%s ", key, value) 139 | } else { 140 | fmt.Fprintf(b, "%v=%q ", key, value) 141 | } 142 | case error: 143 | if needsQuoting(value.(error).Error()) { 144 | fmt.Fprintf(b, "%v=%s ", key, value) 145 | } else { 146 | fmt.Fprintf(b, "%v=%q ", key, value) 147 | } 148 | default: 149 | fmt.Fprintf(b, "%v=%v ", key, value) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestQuoting(t *testing.T) { 11 | tf := &TextFormatter{DisableColors: true} 12 | 13 | checkQuoting := func(q bool, value interface{}) { 14 | b, _ := tf.Format(WithField("test", value)) 15 | idx := bytes.Index(b, ([]byte)("test=")) 16 | cont := bytes.Contains(b[idx+5:], []byte{'"'}) 17 | if cont != q { 18 | if q { 19 | t.Errorf("quoting expected for: %#v", value) 20 | } else { 21 | t.Errorf("quoting not expected for: %#v", value) 22 | } 23 | } 24 | } 25 | 26 | checkQuoting(false, "abcd") 27 | checkQuoting(false, "v1.0") 28 | checkQuoting(false, "1234567890") 29 | checkQuoting(true, "/foobar") 30 | checkQuoting(true, "x y") 31 | checkQuoting(true, "x,y") 32 | checkQuoting(false, errors.New("invalid")) 33 | checkQuoting(true, errors.New("invalid argument")) 34 | } 35 | 36 | func TestTimestampFormat(t *testing.T) { 37 | checkTimeStr := func(format string) { 38 | customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} 39 | customStr, _ := customFormatter.Format(WithField("test", "test")) 40 | timeStart := bytes.Index(customStr, ([]byte)("time=")) 41 | timeEnd := bytes.Index(customStr, ([]byte)("level=")) 42 | timeStr := customStr[timeStart+5 : timeEnd-1] 43 | if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { 44 | timeStr = timeStr[1 : len(timeStr)-1] 45 | } 46 | if format == "" { 47 | format = time.RFC3339 48 | } 49 | _, e := time.Parse(format, (string)(timeStr)) 50 | if e != nil { 51 | t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) 52 | } 53 | } 54 | 55 | checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") 56 | checkTimeStr("Mon Jan _2 15:04:05 2006") 57 | checkTimeStr("") 58 | } 59 | 60 | // TODO add tests for sorting etc., this requires a parser for the text 61 | // formatter output. 62 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "runtime" 7 | ) 8 | 9 | func (logger *Logger) Writer() *io.PipeWriter { 10 | reader, writer := io.Pipe() 11 | 12 | go logger.writerScanner(reader) 13 | runtime.SetFinalizer(writer, writerFinalizer) 14 | 15 | return writer 16 | } 17 | 18 | func (logger *Logger) writerScanner(reader *io.PipeReader) { 19 | scanner := bufio.NewScanner(reader) 20 | for scanner.Scan() { 21 | logger.Print(scanner.Text()) 22 | } 23 | if err := scanner.Err(); err != nil { 24 | logger.Errorf("Error while reading from Writer: %s", err) 25 | } 26 | reader.Close() 27 | } 28 | 29 | func writerFinalizer(writer *io.PipeWriter) { 30 | writer.Close() 31 | } 32 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/distribution/digest/digest.go: -------------------------------------------------------------------------------- 1 | package digest 2 | 3 | import ( 4 | "fmt" 5 | "hash" 6 | "io" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | // DigestSha256EmptyTar is the canonical sha256 digest of empty data 13 | DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" 14 | ) 15 | 16 | // Digest allows simple protection of hex formatted digest strings, prefixed 17 | // by their algorithm. Strings of type Digest have some guarantee of being in 18 | // the correct format and it provides quick access to the components of a 19 | // digest string. 20 | // 21 | // The following is an example of the contents of Digest types: 22 | // 23 | // sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc 24 | // 25 | // This allows to abstract the digest behind this type and work only in those 26 | // terms. 27 | type Digest string 28 | 29 | // NewDigest returns a Digest from alg and a hash.Hash object. 30 | func NewDigest(alg Algorithm, h hash.Hash) Digest { 31 | return NewDigestFromBytes(alg, h.Sum(nil)) 32 | } 33 | 34 | // NewDigestFromBytes returns a new digest from the byte contents of p. 35 | // Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) 36 | // functions. This is also useful for rebuilding digests from binary 37 | // serializations. 38 | func NewDigestFromBytes(alg Algorithm, p []byte) Digest { 39 | return Digest(fmt.Sprintf("%s:%x", alg, p)) 40 | } 41 | 42 | // NewDigestFromHex returns a Digest from alg and a the hex encoded digest. 43 | func NewDigestFromHex(alg, hex string) Digest { 44 | return Digest(fmt.Sprintf("%s:%s", alg, hex)) 45 | } 46 | 47 | // DigestRegexp matches valid digest types. 48 | var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`) 49 | 50 | // DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. 51 | var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) 52 | 53 | var ( 54 | // ErrDigestInvalidFormat returned when digest format invalid. 55 | ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") 56 | 57 | // ErrDigestInvalidLength returned when digest has invalid length. 58 | ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") 59 | 60 | // ErrDigestUnsupported returned when the digest algorithm is unsupported. 61 | ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") 62 | ) 63 | 64 | // ParseDigest parses s and returns the validated digest object. An error will 65 | // be returned if the format is invalid. 66 | func ParseDigest(s string) (Digest, error) { 67 | d := Digest(s) 68 | 69 | return d, d.Validate() 70 | } 71 | 72 | // FromReader returns the most valid digest for the underlying content using 73 | // the canonical digest algorithm. 74 | func FromReader(rd io.Reader) (Digest, error) { 75 | return Canonical.FromReader(rd) 76 | } 77 | 78 | // FromBytes digests the input and returns a Digest. 79 | func FromBytes(p []byte) Digest { 80 | return Canonical.FromBytes(p) 81 | } 82 | 83 | // Validate checks that the contents of d is a valid digest, returning an 84 | // error if not. 85 | func (d Digest) Validate() error { 86 | s := string(d) 87 | 88 | if !DigestRegexpAnchored.MatchString(s) { 89 | return ErrDigestInvalidFormat 90 | } 91 | 92 | i := strings.Index(s, ":") 93 | if i < 0 { 94 | return ErrDigestInvalidFormat 95 | } 96 | 97 | // case: "sha256:" with no hex. 98 | if i+1 == len(s) { 99 | return ErrDigestInvalidFormat 100 | } 101 | 102 | switch algorithm := Algorithm(s[:i]); algorithm { 103 | case SHA256, SHA384, SHA512: 104 | if algorithm.Size()*2 != len(s[i+1:]) { 105 | return ErrDigestInvalidLength 106 | } 107 | break 108 | default: 109 | return ErrDigestUnsupported 110 | } 111 | 112 | return nil 113 | } 114 | 115 | // Algorithm returns the algorithm portion of the digest. This will panic if 116 | // the underlying digest is not in a valid format. 117 | func (d Digest) Algorithm() Algorithm { 118 | return Algorithm(d[:d.sepIndex()]) 119 | } 120 | 121 | // Hex returns the hex digest portion of the digest. This will panic if the 122 | // underlying digest is not in a valid format. 123 | func (d Digest) Hex() string { 124 | return string(d[d.sepIndex()+1:]) 125 | } 126 | 127 | func (d Digest) String() string { 128 | return string(d) 129 | } 130 | 131 | func (d Digest) sepIndex() int { 132 | i := strings.Index(string(d), ":") 133 | 134 | if i < 0 { 135 | panic("could not find ':' in digest: " + d) 136 | } 137 | 138 | return i 139 | } 140 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/distribution/digest/doc.go: -------------------------------------------------------------------------------- 1 | // Package digest provides a generalized type to opaquely represent message 2 | // digests and their operations within the registry. The Digest type is 3 | // designed to serve as a flexible identifier in a content-addressable system. 4 | // More importantly, it provides tools and wrappers to work with 5 | // hash.Hash-based digests with little effort. 6 | // 7 | // Basics 8 | // 9 | // The format of a digest is simply a string with two parts, dubbed the 10 | // "algorithm" and the "digest", separated by a colon: 11 | // 12 | // : 13 | // 14 | // An example of a sha256 digest representation follows: 15 | // 16 | // sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc 17 | // 18 | // In this case, the string "sha256" is the algorithm and the hex bytes are 19 | // the "digest". 20 | // 21 | // Because the Digest type is simply a string, once a valid Digest is 22 | // obtained, comparisons are cheap, quick and simple to express with the 23 | // standard equality operator. 24 | // 25 | // Verification 26 | // 27 | // The main benefit of using the Digest type is simple verification against a 28 | // given digest. The Verifier interface, modeled after the stdlib hash.Hash 29 | // interface, provides a common write sink for digest verification. After 30 | // writing is complete, calling the Verifier.Verified method will indicate 31 | // whether or not the stream of bytes matches the target digest. 32 | // 33 | // Missing Features 34 | // 35 | // In addition to the above, we intend to add the following features to this 36 | // package: 37 | // 38 | // 1. A Digester type that supports write sink digest calculation. 39 | // 40 | // 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry. 41 | // 42 | package digest 43 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/distribution/digest/verifiers.go: -------------------------------------------------------------------------------- 1 | package digest 2 | 3 | import ( 4 | "hash" 5 | "io" 6 | ) 7 | 8 | // Verifier presents a general verification interface to be used with message 9 | // digests and other byte stream verifications. Users instantiate a Verifier 10 | // from one of the various methods, write the data under test to it then check 11 | // the result with the Verified method. 12 | type Verifier interface { 13 | io.Writer 14 | 15 | // Verified will return true if the content written to Verifier matches 16 | // the digest. 17 | Verified() bool 18 | } 19 | 20 | // NewDigestVerifier returns a verifier that compares the written bytes 21 | // against a passed in digest. 22 | func NewDigestVerifier(d Digest) (Verifier, error) { 23 | if err := d.Validate(); err != nil { 24 | return nil, err 25 | } 26 | 27 | return hashVerifier{ 28 | hash: d.Algorithm().Hash(), 29 | digest: d, 30 | }, nil 31 | } 32 | 33 | type hashVerifier struct { 34 | digest Digest 35 | hash hash.Hash 36 | } 37 | 38 | func (hv hashVerifier) Write(p []byte) (n int, err error) { 39 | return hv.hash.Write(p) 40 | } 41 | 42 | func (hv hashVerifier) Verified() bool { 43 | return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash) 44 | } 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/NOTICE: -------------------------------------------------------------------------------- 1 | Docker 2 | Copyright 2012-2016 Docker, Inc. 3 | 4 | This product includes software developed at Docker, Inc. (https://www.docker.com). 5 | 6 | This product contains software (https://github.com/kr/pty) developed 7 | by Keith Rarick, licensed under the MIT License. 8 | 9 | The following is courtesy of our legal counsel: 10 | 11 | 12 | Use and transfer of Docker may be subject to certain restrictions by the 13 | United States and other governments. 14 | It is your responsibility to ensure that your use and/or transfer does not 15 | violate applicable laws. 16 | 17 | For more information, please see https://www.bis.doc.gov 18 | 19 | See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/fileutils/fileutils_unix.go: -------------------------------------------------------------------------------- 1 | // +build linux freebsd 2 | 3 | package fileutils 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | 10 | "github.com/Sirupsen/logrus" 11 | ) 12 | 13 | // GetTotalUsedFds Returns the number of used File Descriptors by 14 | // reading it via /proc filesystem. 15 | func GetTotalUsedFds() int { 16 | if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { 17 | logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) 18 | } else { 19 | return len(fds) 20 | } 21 | return -1 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/fileutils/fileutils_windows.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | // GetTotalUsedFds Returns the number of used File Descriptors. Not supported 4 | // on Windows. 5 | func GetTotalUsedFds() int { 6 | return -1 7 | } 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/fmt.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // FprintfIfNotEmpty prints the string value if it's not empty 9 | func FprintfIfNotEmpty(w io.Writer, format, value string) (int, error) { 10 | if value != "" { 11 | return fmt.Fprintf(w, format, value) 12 | } 13 | return 0, nil 14 | } 15 | 16 | // FprintfIfTrue prints the boolean value if it's true 17 | func FprintfIfTrue(w io.Writer, format string, ok bool) (int, error) { 18 | if ok { 19 | return fmt.Fprintf(w, format, ok) 20 | } 21 | return 0, nil 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "io" 7 | 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | type readCloserWrapper struct { 12 | io.Reader 13 | closer func() error 14 | } 15 | 16 | func (r *readCloserWrapper) Close() error { 17 | return r.closer() 18 | } 19 | 20 | // NewReadCloserWrapper returns a new io.ReadCloser. 21 | func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { 22 | return &readCloserWrapper{ 23 | Reader: r, 24 | closer: closer, 25 | } 26 | } 27 | 28 | type readerErrWrapper struct { 29 | reader io.Reader 30 | closer func() 31 | } 32 | 33 | func (r *readerErrWrapper) Read(p []byte) (int, error) { 34 | n, err := r.reader.Read(p) 35 | if err != nil { 36 | r.closer() 37 | } 38 | return n, err 39 | } 40 | 41 | // NewReaderErrWrapper returns a new io.Reader. 42 | func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader { 43 | return &readerErrWrapper{ 44 | reader: r, 45 | closer: closer, 46 | } 47 | } 48 | 49 | // HashData returns the sha256 sum of src. 50 | func HashData(src io.Reader) (string, error) { 51 | h := sha256.New() 52 | if _, err := io.Copy(h, src); err != nil { 53 | return "", err 54 | } 55 | return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil 56 | } 57 | 58 | // OnEOFReader wraps a io.ReadCloser and a function 59 | // the function will run at the end of file or close the file. 60 | type OnEOFReader struct { 61 | Rc io.ReadCloser 62 | Fn func() 63 | } 64 | 65 | func (r *OnEOFReader) Read(p []byte) (n int, err error) { 66 | n, err = r.Rc.Read(p) 67 | if err == io.EOF { 68 | r.runFunc() 69 | } 70 | return 71 | } 72 | 73 | // Close closes the file and run the function. 74 | func (r *OnEOFReader) Close() error { 75 | err := r.Rc.Close() 76 | r.runFunc() 77 | return err 78 | } 79 | 80 | func (r *OnEOFReader) runFunc() { 81 | if fn := r.Fn; fn != nil { 82 | fn() 83 | r.Fn = nil 84 | } 85 | } 86 | 87 | // cancelReadCloser wraps an io.ReadCloser with a context for cancelling read 88 | // operations. 89 | type cancelReadCloser struct { 90 | cancel func() 91 | pR *io.PipeReader // Stream to read from 92 | pW *io.PipeWriter 93 | } 94 | 95 | // NewCancelReadCloser creates a wrapper that closes the ReadCloser when the 96 | // context is cancelled. The returned io.ReadCloser must be closed when it is 97 | // no longer needed. 98 | func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser { 99 | pR, pW := io.Pipe() 100 | 101 | // Create a context used to signal when the pipe is closed 102 | doneCtx, cancel := context.WithCancel(context.Background()) 103 | 104 | p := &cancelReadCloser{ 105 | cancel: cancel, 106 | pR: pR, 107 | pW: pW, 108 | } 109 | 110 | go func() { 111 | _, err := io.Copy(pW, in) 112 | select { 113 | case <-ctx.Done(): 114 | // If the context was closed, p.closeWithError 115 | // was already called. Calling it again would 116 | // change the error that Read returns. 117 | default: 118 | p.closeWithError(err) 119 | } 120 | in.Close() 121 | }() 122 | go func() { 123 | for { 124 | select { 125 | case <-ctx.Done(): 126 | p.closeWithError(ctx.Err()) 127 | case <-doneCtx.Done(): 128 | return 129 | } 130 | } 131 | }() 132 | 133 | return p 134 | } 135 | 136 | // Read wraps the Read method of the pipe that provides data from the wrapped 137 | // ReadCloser. 138 | func (p *cancelReadCloser) Read(buf []byte) (n int, err error) { 139 | return p.pR.Read(buf) 140 | } 141 | 142 | // closeWithError closes the wrapper and its underlying reader. It will 143 | // cause future calls to Read to return err. 144 | func (p *cancelReadCloser) closeWithError(err error) { 145 | p.pW.CloseWithError(err) 146 | p.cancel() 147 | } 148 | 149 | // Close closes the wrapper its underlying reader. It will cause 150 | // future calls to Read to return io.EOF. 151 | func (p *cancelReadCloser) Close() error { 152 | p.closeWithError(io.EOF) 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/scheduler.go: -------------------------------------------------------------------------------- 1 | // +build !gccgo 2 | 3 | package ioutils 4 | 5 | func callSchedulerIfNecessary() { 6 | } 7 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/scheduler_gccgo.go: -------------------------------------------------------------------------------- 1 | // +build gccgo 2 | 3 | package ioutils 4 | 5 | import ( 6 | "runtime" 7 | ) 8 | 9 | func callSchedulerIfNecessary() { 10 | //allow or force Go scheduler to switch context, without explicitly 11 | //forcing this will make it hang when using gccgo implementation 12 | runtime.Gosched() 13 | } 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/temp_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package ioutils 4 | 5 | import "io/ioutil" 6 | 7 | // TempDir on Unix systems is equivalent to ioutil.TempDir. 8 | func TempDir(dir, prefix string) (string, error) { 9 | return ioutil.TempDir(dir, prefix) 10 | } 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/temp_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package ioutils 4 | 5 | import ( 6 | "io/ioutil" 7 | 8 | "github.com/docker/docker/pkg/longpath" 9 | ) 10 | 11 | // TempDir is the equivalent of ioutil.TempDir, except that the result is in Windows longpath format. 12 | func TempDir(dir, prefix string) (string, error) { 13 | tempDir, err := ioutil.TempDir(dir, prefix) 14 | if err != nil { 15 | return "", err 16 | } 17 | return longpath.AddPrefix(tempDir), nil 18 | } 19 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writeflusher.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net/http" 7 | "sync" 8 | ) 9 | 10 | // WriteFlusher wraps the Write and Flush operation ensuring that every write 11 | // is a flush. In addition, the Close method can be called to intercept 12 | // Read/Write calls if the targets lifecycle has already ended. 13 | type WriteFlusher struct { 14 | mu sync.Mutex 15 | w io.Writer 16 | flusher http.Flusher 17 | flushed bool 18 | closed error 19 | 20 | // TODO(stevvooe): Use channel for closed instead, remove mutex. Using a 21 | // channel will allow one to properly order the operations. 22 | } 23 | 24 | var errWriteFlusherClosed = errors.New("writeflusher: closed") 25 | 26 | func (wf *WriteFlusher) Write(b []byte) (n int, err error) { 27 | wf.mu.Lock() 28 | defer wf.mu.Unlock() 29 | if wf.closed != nil { 30 | return 0, wf.closed 31 | } 32 | 33 | n, err = wf.w.Write(b) 34 | wf.flush() // every write is a flush. 35 | return n, err 36 | } 37 | 38 | // Flush the stream immediately. 39 | func (wf *WriteFlusher) Flush() { 40 | wf.mu.Lock() 41 | defer wf.mu.Unlock() 42 | 43 | wf.flush() 44 | } 45 | 46 | // flush the stream immediately without taking a lock. Used internally. 47 | func (wf *WriteFlusher) flush() { 48 | if wf.closed != nil { 49 | return 50 | } 51 | 52 | wf.flushed = true 53 | wf.flusher.Flush() 54 | } 55 | 56 | // Flushed returns the state of flushed. 57 | // If it's flushed, return true, or else it return false. 58 | func (wf *WriteFlusher) Flushed() bool { 59 | // BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to 60 | // be used to detect whether or a response code has been issued or not. 61 | // Another hook should be used instead. 62 | wf.mu.Lock() 63 | defer wf.mu.Unlock() 64 | 65 | return wf.flushed 66 | } 67 | 68 | // Close closes the write flusher, disallowing any further writes to the 69 | // target. After the flusher is closed, all calls to write or flush will 70 | // result in an error. 71 | func (wf *WriteFlusher) Close() error { 72 | wf.mu.Lock() 73 | defer wf.mu.Unlock() 74 | 75 | if wf.closed != nil { 76 | return wf.closed 77 | } 78 | 79 | wf.closed = errWriteFlusherClosed 80 | return nil 81 | } 82 | 83 | // NewWriteFlusher returns a new WriteFlusher. 84 | func NewWriteFlusher(w io.Writer) *WriteFlusher { 85 | var flusher http.Flusher 86 | if f, ok := w.(http.Flusher); ok { 87 | flusher = f 88 | } else { 89 | flusher = &NopFlusher{} 90 | } 91 | return &WriteFlusher{w: w, flusher: flusher} 92 | } 93 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writers.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import "io" 4 | 5 | // NopWriter represents a type which write operation is nop. 6 | type NopWriter struct{} 7 | 8 | func (*NopWriter) Write(buf []byte) (int, error) { 9 | return len(buf), nil 10 | } 11 | 12 | type nopWriteCloser struct { 13 | io.Writer 14 | } 15 | 16 | func (w *nopWriteCloser) Close() error { return nil } 17 | 18 | // NopWriteCloser returns a nopWriteCloser. 19 | func NopWriteCloser(w io.Writer) io.WriteCloser { 20 | return &nopWriteCloser{w} 21 | } 22 | 23 | // NopFlusher represents a type which flush operation is nop. 24 | type NopFlusher struct{} 25 | 26 | // Flush is a nop operation. 27 | func (f *NopFlusher) Flush() {} 28 | 29 | type writeCloserWrapper struct { 30 | io.Writer 31 | closer func() error 32 | } 33 | 34 | func (r *writeCloserWrapper) Close() error { 35 | return r.closer() 36 | } 37 | 38 | // NewWriteCloserWrapper returns a new io.WriteCloser. 39 | func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser { 40 | return &writeCloserWrapper{ 41 | Writer: r, 42 | closer: closer, 43 | } 44 | } 45 | 46 | // WriteCounter wraps a concrete io.Writer and hold a count of the number 47 | // of bytes written to the writer during a "session". 48 | // This can be convenient when write return is masked 49 | // (e.g., json.Encoder.Encode()) 50 | type WriteCounter struct { 51 | Count int64 52 | Writer io.Writer 53 | } 54 | 55 | // NewWriteCounter returns a new WriteCounter. 56 | func NewWriteCounter(w io.Writer) *WriteCounter { 57 | return &WriteCounter{ 58 | Writer: w, 59 | } 60 | } 61 | 62 | func (wc *WriteCounter) Write(p []byte) (count int, err error) { 63 | count, err = wc.Writer.Write(p) 64 | wc.Count += int64(count) 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/longpath/longpath.go: -------------------------------------------------------------------------------- 1 | // longpath introduces some constants and helper functions for handling long paths 2 | // in Windows, which are expected to be prepended with `\\?\` and followed by either 3 | // a drive letter, a UNC server\share, or a volume identifier. 4 | 5 | package longpath 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // Prefix is the longpath prefix for Windows file paths. 12 | const Prefix = `\\?\` 13 | 14 | // AddPrefix will add the Windows long path prefix to the path provided if 15 | // it does not already have it. 16 | func AddPrefix(path string) string { 17 | if !strings.HasPrefix(path, Prefix) { 18 | if strings.HasPrefix(path, `\\`) { 19 | // This is a UNC path, so we need to add 'UNC' to the path as well. 20 | path = Prefix + `UNC` + path[1:] 21 | } else { 22 | path = Prefix + path 23 | } 24 | } 25 | return path 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/pools/pools.go: -------------------------------------------------------------------------------- 1 | // Package pools provides a collection of pools which provide various 2 | // data types with buffers. These can be used to lower the number of 3 | // memory allocations and reuse buffers. 4 | // 5 | // New pools should be added to this package to allow them to be 6 | // shared across packages. 7 | // 8 | // Utility functions which operate on pools should be added to this 9 | // package to allow them to be reused. 10 | package pools 11 | 12 | import ( 13 | "bufio" 14 | "io" 15 | "sync" 16 | 17 | "github.com/docker/docker/pkg/ioutils" 18 | ) 19 | 20 | var ( 21 | // BufioReader32KPool is a pool which returns bufio.Reader with a 32K buffer. 22 | BufioReader32KPool *BufioReaderPool 23 | // BufioWriter32KPool is a pool which returns bufio.Writer with a 32K buffer. 24 | BufioWriter32KPool *BufioWriterPool 25 | ) 26 | 27 | const buffer32K = 32 * 1024 28 | 29 | // BufioReaderPool is a bufio reader that uses sync.Pool. 30 | type BufioReaderPool struct { 31 | pool sync.Pool 32 | } 33 | 34 | func init() { 35 | BufioReader32KPool = newBufioReaderPoolWithSize(buffer32K) 36 | BufioWriter32KPool = newBufioWriterPoolWithSize(buffer32K) 37 | } 38 | 39 | // newBufioReaderPoolWithSize is unexported because new pools should be 40 | // added here to be shared where required. 41 | func newBufioReaderPoolWithSize(size int) *BufioReaderPool { 42 | pool := sync.Pool{ 43 | New: func() interface{} { return bufio.NewReaderSize(nil, size) }, 44 | } 45 | return &BufioReaderPool{pool: pool} 46 | } 47 | 48 | // Get returns a bufio.Reader which reads from r. The buffer size is that of the pool. 49 | func (bufPool *BufioReaderPool) Get(r io.Reader) *bufio.Reader { 50 | buf := bufPool.pool.Get().(*bufio.Reader) 51 | buf.Reset(r) 52 | return buf 53 | } 54 | 55 | // Put puts the bufio.Reader back into the pool. 56 | func (bufPool *BufioReaderPool) Put(b *bufio.Reader) { 57 | b.Reset(nil) 58 | bufPool.pool.Put(b) 59 | } 60 | 61 | // Copy is a convenience wrapper which uses a buffer to avoid allocation in io.Copy. 62 | func Copy(dst io.Writer, src io.Reader) (written int64, err error) { 63 | buf := BufioReader32KPool.Get(src) 64 | written, err = io.Copy(dst, buf) 65 | BufioReader32KPool.Put(buf) 66 | return 67 | } 68 | 69 | // NewReadCloserWrapper returns a wrapper which puts the bufio.Reader back 70 | // into the pool and closes the reader if it's an io.ReadCloser. 71 | func (bufPool *BufioReaderPool) NewReadCloserWrapper(buf *bufio.Reader, r io.Reader) io.ReadCloser { 72 | return ioutils.NewReadCloserWrapper(r, func() error { 73 | if readCloser, ok := r.(io.ReadCloser); ok { 74 | readCloser.Close() 75 | } 76 | bufPool.Put(buf) 77 | return nil 78 | }) 79 | } 80 | 81 | // BufioWriterPool is a bufio writer that uses sync.Pool. 82 | type BufioWriterPool struct { 83 | pool sync.Pool 84 | } 85 | 86 | // newBufioWriterPoolWithSize is unexported because new pools should be 87 | // added here to be shared where required. 88 | func newBufioWriterPoolWithSize(size int) *BufioWriterPool { 89 | pool := sync.Pool{ 90 | New: func() interface{} { return bufio.NewWriterSize(nil, size) }, 91 | } 92 | return &BufioWriterPool{pool: pool} 93 | } 94 | 95 | // Get returns a bufio.Writer which writes to w. The buffer size is that of the pool. 96 | func (bufPool *BufioWriterPool) Get(w io.Writer) *bufio.Writer { 97 | buf := bufPool.pool.Get().(*bufio.Writer) 98 | buf.Reset(w) 99 | return buf 100 | } 101 | 102 | // Put puts the bufio.Writer back into the pool. 103 | func (bufPool *BufioWriterPool) Put(b *bufio.Writer) { 104 | b.Reset(nil) 105 | bufPool.pool.Put(b) 106 | } 107 | 108 | // NewWriteCloserWrapper returns a wrapper which puts the bufio.Writer back 109 | // into the pool and closes the writer if it's an io.Writecloser. 110 | func (bufPool *BufioWriterPool) NewWriteCloserWrapper(buf *bufio.Writer, w io.Writer) io.WriteCloser { 111 | return ioutils.NewWriteCloserWrapper(w, func() error { 112 | buf.Flush() 113 | if writeCloser, ok := w.(io.WriteCloser); ok { 114 | writeCloser.Close() 115 | } 116 | bufPool.Put(buf) 117 | return nil 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/chtimes.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | "time" 7 | "unsafe" 8 | ) 9 | 10 | var ( 11 | maxTime time.Time 12 | ) 13 | 14 | func init() { 15 | if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 { 16 | // This is a 64 bit timespec 17 | // os.Chtimes limits time to the following 18 | maxTime = time.Unix(0, 1<<63-1) 19 | } else { 20 | // This is a 32 bit timespec 21 | maxTime = time.Unix(1<<31-1, 0) 22 | } 23 | } 24 | 25 | // Chtimes changes the access time and modified time of a file at the given path 26 | func Chtimes(name string, atime time.Time, mtime time.Time) error { 27 | unixMinTime := time.Unix(0, 0) 28 | unixMaxTime := maxTime 29 | 30 | // If the modified time is prior to the Unix Epoch, or after the 31 | // end of Unix Time, os.Chtimes has undefined behavior 32 | // default to Unix Epoch in this case, just in case 33 | 34 | if atime.Before(unixMinTime) || atime.After(unixMaxTime) { 35 | atime = unixMinTime 36 | } 37 | 38 | if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) { 39 | mtime = unixMinTime 40 | } 41 | 42 | if err := os.Chtimes(name, atime, mtime); err != nil { 43 | return err 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/errors.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | // ErrNotSupportedPlatform means the platform is not supported. 9 | ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") 10 | ) 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/events_windows.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | // This file implements syscalls for Win32 events which are not implemented 4 | // in golang. 5 | 6 | import ( 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | var ( 12 | procCreateEvent = modkernel32.NewProc("CreateEventW") 13 | procOpenEvent = modkernel32.NewProc("OpenEventW") 14 | procSetEvent = modkernel32.NewProc("SetEvent") 15 | procResetEvent = modkernel32.NewProc("ResetEvent") 16 | procPulseEvent = modkernel32.NewProc("PulseEvent") 17 | ) 18 | 19 | // CreateEvent implements win32 CreateEventW func in golang. It will create an event object. 20 | func CreateEvent(eventAttributes *syscall.SecurityAttributes, manualReset bool, initialState bool, name string) (handle syscall.Handle, err error) { 21 | namep, _ := syscall.UTF16PtrFromString(name) 22 | var _p1 uint32 23 | if manualReset { 24 | _p1 = 1 25 | } 26 | var _p2 uint32 27 | if initialState { 28 | _p2 = 1 29 | } 30 | r0, _, e1 := procCreateEvent.Call(uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(namep))) 31 | use(unsafe.Pointer(namep)) 32 | handle = syscall.Handle(r0) 33 | if handle == syscall.InvalidHandle { 34 | err = e1 35 | } 36 | return 37 | } 38 | 39 | // OpenEvent implements win32 OpenEventW func in golang. It opens an event object. 40 | func OpenEvent(desiredAccess uint32, inheritHandle bool, name string) (handle syscall.Handle, err error) { 41 | namep, _ := syscall.UTF16PtrFromString(name) 42 | var _p1 uint32 43 | if inheritHandle { 44 | _p1 = 1 45 | } 46 | r0, _, e1 := procOpenEvent.Call(uintptr(desiredAccess), uintptr(_p1), uintptr(unsafe.Pointer(namep))) 47 | use(unsafe.Pointer(namep)) 48 | handle = syscall.Handle(r0) 49 | if handle == syscall.InvalidHandle { 50 | err = e1 51 | } 52 | return 53 | } 54 | 55 | // SetEvent implements win32 SetEvent func in golang. 56 | func SetEvent(handle syscall.Handle) (err error) { 57 | return setResetPulse(handle, procSetEvent) 58 | } 59 | 60 | // ResetEvent implements win32 ResetEvent func in golang. 61 | func ResetEvent(handle syscall.Handle) (err error) { 62 | return setResetPulse(handle, procResetEvent) 63 | } 64 | 65 | // PulseEvent implements win32 PulseEvent func in golang. 66 | func PulseEvent(handle syscall.Handle) (err error) { 67 | return setResetPulse(handle, procPulseEvent) 68 | } 69 | 70 | func setResetPulse(handle syscall.Handle, proc *syscall.LazyProc) (err error) { 71 | r0, _, _ := proc.Call(uintptr(handle)) 72 | if r0 != 0 { 73 | err = syscall.Errno(r0) 74 | } 75 | return 76 | } 77 | 78 | var temp unsafe.Pointer 79 | 80 | // use ensures a variable is kept alive without the GC freeing while still needed 81 | func use(p unsafe.Pointer) { 82 | temp = p 83 | } 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/filesys.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package system 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | // MkdirAll creates a directory named path along with any necessary parents, 11 | // with permission specified by attribute perm for all dir created. 12 | func MkdirAll(path string, perm os.FileMode) error { 13 | return os.MkdirAll(path, perm) 14 | } 15 | 16 | // IsAbs is a platform-specific wrapper for filepath.IsAbs. 17 | func IsAbs(path string) bool { 18 | return filepath.IsAbs(path) 19 | } 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/filesys_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package system 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "regexp" 9 | "strings" 10 | "syscall" 11 | ) 12 | 13 | // MkdirAll implementation that is volume path aware for Windows. 14 | func MkdirAll(path string, perm os.FileMode) error { 15 | if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { 16 | return nil 17 | } 18 | 19 | // The rest of this method is copied from os.MkdirAll and should be kept 20 | // as-is to ensure compatibility. 21 | 22 | // Fast path: if we can tell whether path is a directory or file, stop with success or error. 23 | dir, err := os.Stat(path) 24 | if err == nil { 25 | if dir.IsDir() { 26 | return nil 27 | } 28 | return &os.PathError{ 29 | Op: "mkdir", 30 | Path: path, 31 | Err: syscall.ENOTDIR, 32 | } 33 | } 34 | 35 | // Slow path: make sure parent exists and then call Mkdir for path. 36 | i := len(path) 37 | for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. 38 | i-- 39 | } 40 | 41 | j := i 42 | for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. 43 | j-- 44 | } 45 | 46 | if j > 1 { 47 | // Create parent 48 | err = MkdirAll(path[0:j-1], perm) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | 54 | // Parent now exists; invoke Mkdir and use its result. 55 | err = os.Mkdir(path, perm) 56 | if err != nil { 57 | // Handle arguments like "foo/." by 58 | // double-checking that directory doesn't exist. 59 | dir, err1 := os.Lstat(path) 60 | if err1 == nil && dir.IsDir() { 61 | return nil 62 | } 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | // IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, 69 | // golang filepath.IsAbs does not consider a path \windows\system32 as absolute 70 | // as it doesn't start with a drive-letter/colon combination. However, in 71 | // docker we need to verify things such as WORKDIR /windows/system32 in 72 | // a Dockerfile (which gets translated to \windows\system32 when being processed 73 | // by the daemon. This SHOULD be treated as absolute from a docker processing 74 | // perspective. 75 | func IsAbs(path string) bool { 76 | if !filepath.IsAbs(path) { 77 | if !strings.HasPrefix(path, string(os.PathSeparator)) { 78 | return false 79 | } 80 | } 81 | return true 82 | } 83 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/lstat.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package system 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | // Lstat takes a path to a file and returns 10 | // a system.StatT type pertaining to that file. 11 | // 12 | // Throws an error if the file does not exist 13 | func Lstat(path string) (*StatT, error) { 14 | s := &syscall.Stat_t{} 15 | if err := syscall.Lstat(path, s); err != nil { 16 | return nil, err 17 | } 18 | return fromStatT(s) 19 | } 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/lstat_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package system 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | // Lstat calls os.Lstat to get a fileinfo interface back. 10 | // This is then copied into our own locally defined structure. 11 | // Note the Linux version uses fromStatT to do the copy back, 12 | // but that not strictly necessary when already in an OS specific module. 13 | func Lstat(path string) (*StatT, error) { 14 | fi, err := os.Lstat(path) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return &StatT{ 20 | name: fi.Name(), 21 | size: fi.Size(), 22 | mode: fi.Mode(), 23 | modTime: fi.ModTime(), 24 | isDir: fi.IsDir()}, nil 25 | } 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/meminfo.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | // MemInfo contains memory statistics of the host system. 4 | type MemInfo struct { 5 | // Total usable RAM (i.e. physical RAM minus a few reserved bits and the 6 | // kernel binary code). 7 | MemTotal int64 8 | 9 | // Amount of free memory. 10 | MemFree int64 11 | 12 | // Total amount of swap space available. 13 | SwapTotal int64 14 | 15 | // Amount of swap space that is currently unused. 16 | SwapFree int64 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/meminfo_linux.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/docker/go-units" 11 | ) 12 | 13 | // ReadMemInfo retrieves memory statistics of the host system and returns a 14 | // MemInfo type. 15 | func ReadMemInfo() (*MemInfo, error) { 16 | file, err := os.Open("/proc/meminfo") 17 | if err != nil { 18 | return nil, err 19 | } 20 | defer file.Close() 21 | return parseMemInfo(file) 22 | } 23 | 24 | // parseMemInfo parses the /proc/meminfo file into 25 | // a MemInfo object given a io.Reader to the file. 26 | // 27 | // Throws error if there are problems reading from the file 28 | func parseMemInfo(reader io.Reader) (*MemInfo, error) { 29 | meminfo := &MemInfo{} 30 | scanner := bufio.NewScanner(reader) 31 | for scanner.Scan() { 32 | // Expected format: ["MemTotal:", "1234", "kB"] 33 | parts := strings.Fields(scanner.Text()) 34 | 35 | // Sanity checks: Skip malformed entries. 36 | if len(parts) < 3 || parts[2] != "kB" { 37 | continue 38 | } 39 | 40 | // Convert to bytes. 41 | size, err := strconv.Atoi(parts[1]) 42 | if err != nil { 43 | continue 44 | } 45 | bytes := int64(size) * units.KiB 46 | 47 | switch parts[0] { 48 | case "MemTotal:": 49 | meminfo.MemTotal = bytes 50 | case "MemFree:": 51 | meminfo.MemFree = bytes 52 | case "SwapTotal:": 53 | meminfo.SwapTotal = bytes 54 | case "SwapFree:": 55 | meminfo.SwapFree = bytes 56 | } 57 | 58 | } 59 | 60 | // Handle errors that may have occurred during the reading of the file. 61 | if err := scanner.Err(); err != nil { 62 | return nil, err 63 | } 64 | 65 | return meminfo, nil 66 | } 67 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/meminfo_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!windows 2 | 3 | package system 4 | 5 | // ReadMemInfo is not supported on platforms other than linux and windows. 6 | func ReadMemInfo() (*MemInfo, error) { 7 | return nil, ErrNotSupportedPlatform 8 | } 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/meminfo_windows.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | var ( 9 | modkernel32 = syscall.NewLazyDLL("kernel32.dll") 10 | 11 | procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") 12 | ) 13 | 14 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx 15 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx 16 | type memorystatusex struct { 17 | dwLength uint32 18 | dwMemoryLoad uint32 19 | ullTotalPhys uint64 20 | ullAvailPhys uint64 21 | ullTotalPageFile uint64 22 | ullAvailPageFile uint64 23 | ullTotalVirtual uint64 24 | ullAvailVirtual uint64 25 | ullAvailExtendedVirtual uint64 26 | } 27 | 28 | // ReadMemInfo retrieves memory statistics of the host system and returns a 29 | // MemInfo type. 30 | func ReadMemInfo() (*MemInfo, error) { 31 | msi := &memorystatusex{ 32 | dwLength: 64, 33 | } 34 | r1, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msi))) 35 | if r1 == 0 { 36 | return &MemInfo{}, nil 37 | } 38 | return &MemInfo{ 39 | MemTotal: int64(msi.ullTotalPhys), 40 | MemFree: int64(msi.ullAvailPhys), 41 | SwapTotal: int64(msi.ullTotalPageFile), 42 | SwapFree: int64(msi.ullAvailPageFile), 43 | }, nil 44 | } 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/mknod.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package system 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | // Mknod creates a filesystem node (file, device special file or named pipe) named path 10 | // with attributes specified by mode and dev. 11 | func Mknod(path string, mode uint32, dev int) error { 12 | return syscall.Mknod(path, mode, dev) 13 | } 14 | 15 | // Mkdev is used to build the value of linux devices (in /dev/) which specifies major 16 | // and minor number of the newly created device special file. 17 | // Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. 18 | // They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, 19 | // then the top 12 bits of the minor. 20 | func Mkdev(major int64, minor int64) uint32 { 21 | return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff)) 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/mknod_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package system 4 | 5 | // Mknod is not implemented on Windows. 6 | func Mknod(path string, mode uint32, dev int) error { 7 | return ErrNotSupportedPlatform 8 | } 9 | 10 | // Mkdev is not implemented on Windows. 11 | func Mkdev(major int64, minor int64) uint32 { 12 | panic("Mkdev not implemented on Windows.") 13 | } 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/path_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package system 4 | 5 | // DefaultPathEnv is unix style list of directories to search for 6 | // executables. Each directory is separated from the next by a colon 7 | // ':' character . 8 | const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/path_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package system 4 | 5 | // DefaultPathEnv is deliberately empty on Windows as the default path will be set by 6 | // the container. Docker has no context of what the default path should be. 7 | const DefaultPathEnv = "" 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package system 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | // StatT type contains status of a file. It contains metadata 10 | // like permission, owner, group, size, etc about a file. 11 | type StatT struct { 12 | mode uint32 13 | uid uint32 14 | gid uint32 15 | rdev uint64 16 | size int64 17 | mtim syscall.Timespec 18 | } 19 | 20 | // Mode returns file's permission mode. 21 | func (s StatT) Mode() uint32 { 22 | return s.mode 23 | } 24 | 25 | // UID returns file's user id of owner. 26 | func (s StatT) UID() uint32 { 27 | return s.uid 28 | } 29 | 30 | // GID returns file's group id of owner. 31 | func (s StatT) GID() uint32 { 32 | return s.gid 33 | } 34 | 35 | // Rdev returns file's device ID (if it's special file). 36 | func (s StatT) Rdev() uint64 { 37 | return s.rdev 38 | } 39 | 40 | // Size returns file's size. 41 | func (s StatT) Size() int64 { 42 | return s.size 43 | } 44 | 45 | // Mtim returns file's last modification time. 46 | func (s StatT) Mtim() syscall.Timespec { 47 | return s.mtim 48 | } 49 | 50 | // GetLastModification returns file's last modification time. 51 | func (s StatT) GetLastModification() syscall.Timespec { 52 | return s.Mtim() 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_freebsd.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | // fromStatT converts a syscall.Stat_t type to a system.Stat_t type 8 | func fromStatT(s *syscall.Stat_t) (*StatT, error) { 9 | return &StatT{size: s.Size, 10 | mode: uint32(s.Mode), 11 | uid: s.Uid, 12 | gid: s.Gid, 13 | rdev: uint64(s.Rdev), 14 | mtim: s.Mtimespec}, nil 15 | } 16 | 17 | // Stat takes a path to a file and returns 18 | // a system.Stat_t type pertaining to that file. 19 | // 20 | // Throws an error if the file does not exist 21 | func Stat(path string) (*StatT, error) { 22 | s := &syscall.Stat_t{} 23 | if err := syscall.Stat(path, s); err != nil { 24 | return nil, err 25 | } 26 | return fromStatT(s) 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_linux.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | // fromStatT converts a syscall.Stat_t type to a system.Stat_t type 8 | func fromStatT(s *syscall.Stat_t) (*StatT, error) { 9 | return &StatT{size: s.Size, 10 | mode: s.Mode, 11 | uid: s.Uid, 12 | gid: s.Gid, 13 | rdev: s.Rdev, 14 | mtim: s.Mtim}, nil 15 | } 16 | 17 | // FromStatT exists only on linux, and loads a system.StatT from a 18 | // syscal.Stat_t. 19 | func FromStatT(s *syscall.Stat_t) (*StatT, error) { 20 | return fromStatT(s) 21 | } 22 | 23 | // Stat takes a path to a file and returns 24 | // a system.StatT type pertaining to that file. 25 | // 26 | // Throws an error if the file does not exist 27 | func Stat(path string) (*StatT, error) { 28 | s := &syscall.Stat_t{} 29 | if err := syscall.Stat(path, s); err != nil { 30 | return nil, err 31 | } 32 | return fromStatT(s) 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_solaris.go: -------------------------------------------------------------------------------- 1 | // +build solaris 2 | 3 | package system 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | // fromStatT creates a system.StatT type from a syscall.Stat_t type 10 | func fromStatT(s *syscall.Stat_t) (*StatT, error) { 11 | return &StatT{size: s.Size, 12 | mode: uint32(s.Mode), 13 | uid: s.Uid, 14 | gid: s.Gid, 15 | rdev: uint64(s.Rdev), 16 | mtim: s.Mtim}, nil 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!windows,!freebsd,!solaris 2 | 3 | package system 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | // fromStatT creates a system.StatT type from a syscall.Stat_t type 10 | func fromStatT(s *syscall.Stat_t) (*StatT, error) { 11 | return &StatT{size: s.Size, 12 | mode: uint32(s.Mode), 13 | uid: s.Uid, 14 | gid: s.Gid, 15 | rdev: uint64(s.Rdev), 16 | mtim: s.Mtimespec}, nil 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package system 4 | 5 | import ( 6 | "os" 7 | "time" 8 | ) 9 | 10 | // StatT type contains status of a file. It contains metadata 11 | // like name, permission, size, etc about a file. 12 | type StatT struct { 13 | name string 14 | size int64 15 | mode os.FileMode 16 | modTime time.Time 17 | isDir bool 18 | } 19 | 20 | // Name returns file's name. 21 | func (s StatT) Name() string { 22 | return s.name 23 | } 24 | 25 | // Size returns file's size. 26 | func (s StatT) Size() int64 { 27 | return s.size 28 | } 29 | 30 | // Mode returns file's permission mode. 31 | func (s StatT) Mode() os.FileMode { 32 | return s.mode 33 | } 34 | 35 | // ModTime returns file's last modification time. 36 | func (s StatT) ModTime() time.Time { 37 | return s.modTime 38 | } 39 | 40 | // IsDir returns whether file is actually a directory. 41 | func (s StatT) IsDir() bool { 42 | return s.isDir 43 | } 44 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/syscall_unix.go: -------------------------------------------------------------------------------- 1 | // +build linux freebsd 2 | 3 | package system 4 | 5 | import "syscall" 6 | 7 | // Unmount is a platform-specific helper function to call 8 | // the unmount syscall. 9 | func Unmount(dest string) error { 10 | return syscall.Unmount(dest, 0) 11 | } 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/syscall_windows.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | ) 7 | 8 | // OSVersion is a wrapper for Windows version information 9 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx 10 | type OSVersion struct { 11 | Version uint32 12 | MajorVersion uint8 13 | MinorVersion uint8 14 | Build uint16 15 | } 16 | 17 | // GetOSVersion gets the operating system version on Windows. Note that 18 | // docker.exe must be manifested to get the correct version information. 19 | func GetOSVersion() (OSVersion, error) { 20 | var err error 21 | osv := OSVersion{} 22 | osv.Version, err = syscall.GetVersion() 23 | if err != nil { 24 | return osv, fmt.Errorf("Failed to call GetVersion()") 25 | } 26 | osv.MajorVersion = uint8(osv.Version & 0xFF) 27 | osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) 28 | osv.Build = uint16(osv.Version >> 16) 29 | return osv, nil 30 | } 31 | 32 | // Unmount is a platform-specific helper function to call 33 | // the unmount syscall. Not supported on Windows 34 | func Unmount(dest string) error { 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/umask.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package system 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | // Umask sets current process's file mode creation mask to newmask 10 | // and return oldmask. 11 | func Umask(newmask int) (oldmask int, err error) { 12 | return syscall.Umask(newmask), nil 13 | } 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/umask_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package system 4 | 5 | // Umask is not supported on the windows platform. 6 | func Umask(newmask int) (oldmask int, err error) { 7 | // should not be called on cli code path 8 | return 0, ErrNotSupportedPlatform 9 | } 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_darwin.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import "syscall" 4 | 5 | // LUtimesNano is not supported by darwin platform. 6 | func LUtimesNano(path string, ts []syscall.Timespec) error { 7 | return ErrNotSupportedPlatform 8 | } 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_freebsd.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | // LUtimesNano is used to change access and modification time of the specified path. 9 | // It's used for symbol link file because syscall.UtimesNano doesn't support a NOFOLLOW flag atm. 10 | func LUtimesNano(path string, ts []syscall.Timespec) error { 11 | var _path *byte 12 | _path, err := syscall.BytePtrFromString(path) 13 | if err != nil { 14 | return err 15 | } 16 | 17 | if _, _, err := syscall.Syscall(syscall.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != syscall.ENOSYS { 18 | return err 19 | } 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_linux.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | // LUtimesNano is used to change access and modification time of the specified path. 9 | // It's used for symbol link file because syscall.UtimesNano doesn't support a NOFOLLOW flag atm. 10 | func LUtimesNano(path string, ts []syscall.Timespec) error { 11 | // These are not currently available in syscall 12 | atFdCwd := -100 13 | atSymLinkNoFollow := 0x100 14 | 15 | var _path *byte 16 | _path, err := syscall.BytePtrFromString(path) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | if _, _, err := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(atFdCwd), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), uintptr(atSymLinkNoFollow), 0, 0); err != 0 && err != syscall.ENOSYS { 22 | return err 23 | } 24 | 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!freebsd,!darwin 2 | 3 | package system 4 | 5 | import "syscall" 6 | 7 | // LUtimesNano is not supported on platforms other than linux, freebsd and darwin. 8 | func LUtimesNano(path string, ts []syscall.Timespec) error { 9 | return ErrNotSupportedPlatform 10 | } 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/xattrs_linux.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | // Lgetxattr retrieves the value of the extended attribute identified by attr 9 | // and associated with the given path in the file system. 10 | // It will returns a nil slice and nil error if the xattr is not set. 11 | func Lgetxattr(path string, attr string) ([]byte, error) { 12 | pathBytes, err := syscall.BytePtrFromString(path) 13 | if err != nil { 14 | return nil, err 15 | } 16 | attrBytes, err := syscall.BytePtrFromString(attr) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | dest := make([]byte, 128) 22 | destBytes := unsafe.Pointer(&dest[0]) 23 | sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) 24 | if errno == syscall.ENODATA { 25 | return nil, nil 26 | } 27 | if errno == syscall.ERANGE { 28 | dest = make([]byte, sz) 29 | destBytes := unsafe.Pointer(&dest[0]) 30 | sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) 31 | } 32 | if errno != 0 { 33 | return nil, errno 34 | } 35 | 36 | return dest[:sz], nil 37 | } 38 | 39 | var _zero uintptr 40 | 41 | // Lsetxattr sets the value of the extended attribute identified by attr 42 | // and associated with the given path in the file system. 43 | func Lsetxattr(path string, attr string, data []byte, flags int) error { 44 | pathBytes, err := syscall.BytePtrFromString(path) 45 | if err != nil { 46 | return err 47 | } 48 | attrBytes, err := syscall.BytePtrFromString(attr) 49 | if err != nil { 50 | return err 51 | } 52 | var dataBytes unsafe.Pointer 53 | if len(data) > 0 { 54 | dataBytes = unsafe.Pointer(&data[0]) 55 | } else { 56 | dataBytes = unsafe.Pointer(&_zero) 57 | } 58 | _, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0) 59 | if errno != 0 { 60 | return errno 61 | } 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/system/xattrs_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | package system 4 | 5 | // Lgetxattr is not supported on platforms other than linux. 6 | func Lgetxattr(path string, attr string) ([]byte, error) { 7 | return nil, ErrNotSupportedPlatform 8 | } 9 | 10 | // Lsetxattr is not supported on platforms other than linux. 11 | func Lsetxattr(path string, attr string, data []byte, flags int) error { 12 | return ErrNotSupportedPlatform 13 | } 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/units/duration.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // HumanDuration returns a human-readable approximation of a duration 9 | // (eg. "About a minute", "4 hours ago", etc.) 10 | func HumanDuration(d time.Duration) string { 11 | if seconds := int(d.Seconds()); seconds < 1 { 12 | return "Less than a second" 13 | } else if seconds < 60 { 14 | return fmt.Sprintf("%d seconds", seconds) 15 | } else if minutes := int(d.Minutes()); minutes == 1 { 16 | return "About a minute" 17 | } else if minutes < 60 { 18 | return fmt.Sprintf("%d minutes", minutes) 19 | } else if hours := int(d.Hours()); hours == 1 { 20 | return "About an hour" 21 | } else if hours < 48 { 22 | return fmt.Sprintf("%d hours", hours) 23 | } else if hours < 24*7*2 { 24 | return fmt.Sprintf("%d days", hours/24) 25 | } else if hours < 24*30*3 { 26 | return fmt.Sprintf("%d weeks", hours/24/7) 27 | } else if hours < 24*365*2 { 28 | return fmt.Sprintf("%d months", hours/24/30) 29 | } 30 | return fmt.Sprintf("%d years", int(d.Hours())/24/365) 31 | } 32 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/units/duration_test.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestHumanDuration(t *testing.T) { 9 | // Useful duration abstractions 10 | day := 24 * time.Hour 11 | week := 7 * day 12 | month := 30 * day 13 | year := 365 * day 14 | 15 | assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond)) 16 | assertEquals(t, "47 seconds", HumanDuration(47*time.Second)) 17 | assertEquals(t, "About a minute", HumanDuration(1*time.Minute)) 18 | assertEquals(t, "3 minutes", HumanDuration(3*time.Minute)) 19 | assertEquals(t, "35 minutes", HumanDuration(35*time.Minute)) 20 | assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second)) 21 | assertEquals(t, "About an hour", HumanDuration(1*time.Hour)) 22 | assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute)) 23 | assertEquals(t, "3 hours", HumanDuration(3*time.Hour)) 24 | assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute)) 25 | assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute)) 26 | assertEquals(t, "24 hours", HumanDuration(24*time.Hour)) 27 | assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour)) 28 | assertEquals(t, "2 days", HumanDuration(2*day)) 29 | assertEquals(t, "7 days", HumanDuration(7*day)) 30 | assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour)) 31 | assertEquals(t, "2 weeks", HumanDuration(2*week)) 32 | assertEquals(t, "2 weeks", HumanDuration(2*week+4*day)) 33 | assertEquals(t, "3 weeks", HumanDuration(3*week)) 34 | assertEquals(t, "4 weeks", HumanDuration(4*week)) 35 | assertEquals(t, "4 weeks", HumanDuration(4*week+3*day)) 36 | assertEquals(t, "4 weeks", HumanDuration(1*month)) 37 | assertEquals(t, "6 weeks", HumanDuration(1*month+2*week)) 38 | assertEquals(t, "8 weeks", HumanDuration(2*month)) 39 | assertEquals(t, "3 months", HumanDuration(3*month+1*week)) 40 | assertEquals(t, "5 months", HumanDuration(5*month+2*week)) 41 | assertEquals(t, "13 months", HumanDuration(13*month)) 42 | assertEquals(t, "23 months", HumanDuration(23*month)) 43 | assertEquals(t, "24 months", HumanDuration(24*month)) 44 | assertEquals(t, "2 years", HumanDuration(24*month+2*week)) 45 | assertEquals(t, "3 years", HumanDuration(3*year+2*month)) 46 | } 47 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/units/size.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // See: http://en.wikipedia.org/wiki/Binary_prefix 11 | const ( 12 | // Decimal 13 | 14 | KB = 1000 15 | MB = 1000 * KB 16 | GB = 1000 * MB 17 | TB = 1000 * GB 18 | PB = 1000 * TB 19 | 20 | // Binary 21 | 22 | KiB = 1024 23 | MiB = 1024 * KiB 24 | GiB = 1024 * MiB 25 | TiB = 1024 * GiB 26 | PiB = 1024 * TiB 27 | ) 28 | 29 | type unitMap map[string]int64 30 | 31 | var ( 32 | decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} 33 | binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} 34 | sizeRegex = regexp.MustCompile(`^(\d+)([kKmMgGtTpP])?[bB]?$`) 35 | ) 36 | 37 | var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} 38 | var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} 39 | 40 | // CustomSize returns a human-readable approximation of a size 41 | // using custom format 42 | func CustomSize(format string, size float64, base float64, _map []string) string { 43 | i := 0 44 | for size >= base { 45 | size = size / base 46 | i++ 47 | } 48 | return fmt.Sprintf(format, size, _map[i]) 49 | } 50 | 51 | // HumanSize returns a human-readable approximation of a size 52 | // using SI standard (eg. "44kB", "17MB") 53 | func HumanSize(size float64) string { 54 | return CustomSize("%.4g %s", float64(size), 1000.0, decimapAbbrs) 55 | } 56 | 57 | func BytesSize(size float64) string { 58 | return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) 59 | } 60 | 61 | // FromHumanSize returns an integer from a human-readable specification of a 62 | // size using SI standard (eg. "44kB", "17MB") 63 | func FromHumanSize(size string) (int64, error) { 64 | return parseSize(size, decimalMap) 65 | } 66 | 67 | // RAMInBytes parses a human-readable string representing an amount of RAM 68 | // in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and 69 | // returns the number of bytes, or -1 if the string is unparseable. 70 | // Units are case-insensitive, and the 'b' suffix is optional. 71 | func RAMInBytes(size string) (int64, error) { 72 | return parseSize(size, binaryMap) 73 | } 74 | 75 | // Parses the human-readable size string into the amount it represents 76 | func parseSize(sizeStr string, uMap unitMap) (int64, error) { 77 | matches := sizeRegex.FindStringSubmatch(sizeStr) 78 | if len(matches) != 3 { 79 | return -1, fmt.Errorf("invalid size: '%s'", sizeStr) 80 | } 81 | 82 | size, err := strconv.ParseInt(matches[1], 10, 0) 83 | if err != nil { 84 | return -1, err 85 | } 86 | 87 | unitPrefix := strings.ToLower(matches[2]) 88 | if mul, ok := uMap[unitPrefix]; ok { 89 | size *= mul 90 | } 91 | 92 | return size, nil 93 | } 94 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/docker/pkg/units/size_test.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "reflect" 5 | "runtime" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestBytesSize(t *testing.T) { 11 | assertEquals(t, "1 KiB", BytesSize(1024)) 12 | assertEquals(t, "1 MiB", BytesSize(1024*1024)) 13 | assertEquals(t, "1 MiB", BytesSize(1048576)) 14 | assertEquals(t, "2 MiB", BytesSize(2*MiB)) 15 | assertEquals(t, "3.42 GiB", BytesSize(3.42*GiB)) 16 | assertEquals(t, "5.372 TiB", BytesSize(5.372*TiB)) 17 | assertEquals(t, "2.22 PiB", BytesSize(2.22*PiB)) 18 | } 19 | 20 | func TestHumanSize(t *testing.T) { 21 | assertEquals(t, "1 kB", HumanSize(1000)) 22 | assertEquals(t, "1.024 kB", HumanSize(1024)) 23 | assertEquals(t, "1 MB", HumanSize(1000000)) 24 | assertEquals(t, "1.049 MB", HumanSize(1048576)) 25 | assertEquals(t, "2 MB", HumanSize(2*MB)) 26 | assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB))) 27 | assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB))) 28 | assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB))) 29 | } 30 | 31 | func TestFromHumanSize(t *testing.T) { 32 | assertSuccessEquals(t, 32, FromHumanSize, "32") 33 | assertSuccessEquals(t, 32, FromHumanSize, "32b") 34 | assertSuccessEquals(t, 32, FromHumanSize, "32B") 35 | assertSuccessEquals(t, 32*KB, FromHumanSize, "32k") 36 | assertSuccessEquals(t, 32*KB, FromHumanSize, "32K") 37 | assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb") 38 | assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb") 39 | assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb") 40 | assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb") 41 | assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb") 42 | assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb") 43 | 44 | assertError(t, FromHumanSize, "") 45 | assertError(t, FromHumanSize, "hello") 46 | assertError(t, FromHumanSize, "-32") 47 | assertError(t, FromHumanSize, "32.3") 48 | assertError(t, FromHumanSize, " 32 ") 49 | assertError(t, FromHumanSize, "32.3Kb") 50 | assertError(t, FromHumanSize, "32 mb") 51 | assertError(t, FromHumanSize, "32m b") 52 | assertError(t, FromHumanSize, "32bm") 53 | } 54 | 55 | func TestRAMInBytes(t *testing.T) { 56 | assertSuccessEquals(t, 32, RAMInBytes, "32") 57 | assertSuccessEquals(t, 32, RAMInBytes, "32b") 58 | assertSuccessEquals(t, 32, RAMInBytes, "32B") 59 | assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k") 60 | assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K") 61 | assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb") 62 | assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb") 63 | assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb") 64 | assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb") 65 | assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb") 66 | assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb") 67 | assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB") 68 | assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P") 69 | 70 | assertError(t, RAMInBytes, "") 71 | assertError(t, RAMInBytes, "hello") 72 | assertError(t, RAMInBytes, "-32") 73 | assertError(t, RAMInBytes, "32.3") 74 | assertError(t, RAMInBytes, " 32 ") 75 | assertError(t, RAMInBytes, "32.3Kb") 76 | assertError(t, RAMInBytes, "32 mb") 77 | assertError(t, RAMInBytes, "32m b") 78 | assertError(t, RAMInBytes, "32bm") 79 | } 80 | 81 | func assertEquals(t *testing.T, expected, actual interface{}) { 82 | if expected != actual { 83 | t.Errorf("Expected '%v' but got '%v'", expected, actual) 84 | } 85 | } 86 | 87 | // func that maps to the parse function signatures as testing abstraction 88 | type parseFn func(string) (int64, error) 89 | 90 | // Define 'String()' for pretty-print 91 | func (fn parseFn) String() string { 92 | fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() 93 | return fnName[strings.LastIndex(fnName, ".")+1:] 94 | } 95 | 96 | func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) { 97 | res, err := fn(arg) 98 | if err != nil || res != expected { 99 | t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err) 100 | } 101 | } 102 | 103 | func assertError(t *testing.T, fn parseFn, arg string) { 104 | res, err := fn(arg) 105 | if err == nil && res != -1 { 106 | t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to go-units 2 | 3 | Want to hack on go-units? Awesome! Here are instructions to get you started. 4 | 5 | go-units is a part of the [Docker](https://www.docker.com) project, and follows 6 | the same rules and principles. If you're already familiar with the way 7 | Docker does things, you'll feel right at home. 8 | 9 | Otherwise, go read Docker's 10 | [contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), 11 | [issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), 12 | [review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and 13 | [branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). 14 | 15 | ### Sign your work 16 | 17 | The sign-off is a simple line at the end of the explanation for the patch. Your 18 | signature certifies that you wrote the patch or otherwise have the right to pass 19 | it on as an open-source patch. The rules are pretty simple: if you can certify 20 | the below (from [developercertificate.org](http://developercertificate.org/)): 21 | 22 | ``` 23 | Developer Certificate of Origin 24 | Version 1.1 25 | 26 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 27 | 660 York Street, Suite 102, 28 | San Francisco, CA 94110 USA 29 | 30 | Everyone is permitted to copy and distribute verbatim copies of this 31 | license document, but changing it is not allowed. 32 | 33 | Developer's Certificate of Origin 1.1 34 | 35 | By making a contribution to this project, I certify that: 36 | 37 | (a) The contribution was created in whole or in part by me and I 38 | have the right to submit it under the open source license 39 | indicated in the file; or 40 | 41 | (b) The contribution is based upon previous work that, to the best 42 | of my knowledge, is covered under an appropriate open source 43 | license and I have the right under that license to submit that 44 | work with modifications, whether created in whole or in part 45 | by me, under the same open source license (unless I am 46 | permitted to submit under a different license), as indicated 47 | in the file; or 48 | 49 | (c) The contribution was provided directly to me by some other 50 | person who certified (a), (b) or (c) and I have not modified 51 | it. 52 | 53 | (d) I understand and agree that this project and the contribution 54 | are public and that a record of the contribution (including all 55 | personal information I submit with it, including my sign-off) is 56 | maintained indefinitely and may be redistributed consistent with 57 | this project or the open source license(s) involved. 58 | ``` 59 | 60 | Then you just add a line to every git commit message: 61 | 62 | Signed-off-by: Joe Smith 63 | 64 | Use your real name (sorry, no pseudonyms or anonymous contributions.) 65 | 66 | If you set your `user.name` and `user.email` git configs, you can sign your 67 | commit automatically with `git commit -s`. 68 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/MAINTAINERS: -------------------------------------------------------------------------------- 1 | # go-connections maintainers file 2 | # 3 | # This file describes who runs the docker/go-connections project and how. 4 | # This is a living document - if you see something out of date or missing, speak up! 5 | # 6 | # It is structured to be consumable by both humans and programs. 7 | # To extract its contents programmatically, use any TOML-compliant parser. 8 | # 9 | # This file is compiled into the MAINTAINERS file in docker/opensource. 10 | # 11 | [Org] 12 | [Org."Core maintainers"] 13 | people = [ 14 | "calavera", 15 | ] 16 | 17 | [people] 18 | 19 | # A reference list of all people associated with the project. 20 | # All other sections should refer to people by their canonical key 21 | # in the people section. 22 | 23 | # ADD YOURSELF HERE IN ALPHABETICAL ORDER 24 | [people.calavera] 25 | Name = "David Calavera" 26 | Email = "david.calavera@gmail.com" 27 | GitHub = "calavera" 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) 2 | 3 | # Introduction 4 | 5 | go-units is a library to transform human friendly measurements into machine friendly values. 6 | 7 | ## Usage 8 | 9 | See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. 10 | 11 | ## Copyright and license 12 | 13 | Copyright © 2015 Docker, Inc. All rights reserved, except as follows. Code 14 | is released under the Apache 2.0 license. The README.md file, and files in the 15 | "docs" folder are licensed under the Creative Commons Attribution 4.0 16 | International License under the terms and conditions set forth in the file 17 | "LICENSE.docs". You may obtain a duplicate copy of the same license, titled 18 | CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. 19 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | post: 3 | # install golint 4 | - go get github.com/golang/lint/golint 5 | 6 | test: 7 | pre: 8 | # run analysis before tests 9 | - go vet ./... 10 | - test -z "$(golint ./... | tee /dev/stderr)" 11 | - test -z "$(gofmt -s -l . | tee /dev/stderr)" 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/duration.go: -------------------------------------------------------------------------------- 1 | // Package units provides helper function to parse and print size and time units 2 | // in human-readable format. 3 | package units 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | // HumanDuration returns a human-readable approximation of a duration 11 | // (eg. "About a minute", "4 hours ago", etc.). 12 | func HumanDuration(d time.Duration) string { 13 | if seconds := int(d.Seconds()); seconds < 1 { 14 | return "Less than a second" 15 | } else if seconds < 60 { 16 | return fmt.Sprintf("%d seconds", seconds) 17 | } else if minutes := int(d.Minutes()); minutes == 1 { 18 | return "About a minute" 19 | } else if minutes < 60 { 20 | return fmt.Sprintf("%d minutes", minutes) 21 | } else if hours := int(d.Hours()); hours == 1 { 22 | return "About an hour" 23 | } else if hours < 48 { 24 | return fmt.Sprintf("%d hours", hours) 25 | } else if hours < 24*7*2 { 26 | return fmt.Sprintf("%d days", hours/24) 27 | } else if hours < 24*30*3 { 28 | return fmt.Sprintf("%d weeks", hours/24/7) 29 | } else if hours < 24*365*2 { 30 | return fmt.Sprintf("%d months", hours/24/30) 31 | } 32 | return fmt.Sprintf("%d years", int(d.Hours())/24/365) 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/size.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // See: http://en.wikipedia.org/wiki/Binary_prefix 11 | const ( 12 | // Decimal 13 | 14 | KB = 1000 15 | MB = 1000 * KB 16 | GB = 1000 * MB 17 | TB = 1000 * GB 18 | PB = 1000 * TB 19 | 20 | // Binary 21 | 22 | KiB = 1024 23 | MiB = 1024 * KiB 24 | GiB = 1024 * MiB 25 | TiB = 1024 * GiB 26 | PiB = 1024 * TiB 27 | ) 28 | 29 | type unitMap map[string]int64 30 | 31 | var ( 32 | decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} 33 | binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} 34 | sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`) 35 | ) 36 | 37 | var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} 38 | var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} 39 | 40 | // CustomSize returns a human-readable approximation of a size 41 | // using custom format. 42 | func CustomSize(format string, size float64, base float64, _map []string) string { 43 | i := 0 44 | for size >= base { 45 | size = size / base 46 | i++ 47 | } 48 | return fmt.Sprintf(format, size, _map[i]) 49 | } 50 | 51 | // HumanSize returns a human-readable approximation of a size 52 | // capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). 53 | func HumanSize(size float64) string { 54 | return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs) 55 | } 56 | 57 | // BytesSize returns a human-readable size in bytes, kibibytes, 58 | // mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). 59 | func BytesSize(size float64) string { 60 | return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) 61 | } 62 | 63 | // FromHumanSize returns an integer from a human-readable specification of a 64 | // size using SI standard (eg. "44kB", "17MB"). 65 | func FromHumanSize(size string) (int64, error) { 66 | return parseSize(size, decimalMap) 67 | } 68 | 69 | // RAMInBytes parses a human-readable string representing an amount of RAM 70 | // in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and 71 | // returns the number of bytes, or -1 if the string is unparseable. 72 | // Units are case-insensitive, and the 'b' suffix is optional. 73 | func RAMInBytes(size string) (int64, error) { 74 | return parseSize(size, binaryMap) 75 | } 76 | 77 | // Parses the human-readable size string into the amount it represents. 78 | func parseSize(sizeStr string, uMap unitMap) (int64, error) { 79 | matches := sizeRegex.FindStringSubmatch(sizeStr) 80 | if len(matches) != 4 { 81 | return -1, fmt.Errorf("invalid size: '%s'", sizeStr) 82 | } 83 | 84 | size, err := strconv.ParseFloat(matches[1], 64) 85 | if err != nil { 86 | return -1, err 87 | } 88 | 89 | unitPrefix := strings.ToLower(matches[3]) 90 | if mul, ok := uMap[unitPrefix]; ok { 91 | size *= float64(mul) 92 | } 93 | 94 | return int64(size), nil 95 | } 96 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/docker/go-units/ulimit.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // Ulimit is a human friendly version of Rlimit. 10 | type Ulimit struct { 11 | Name string 12 | Hard int64 13 | Soft int64 14 | } 15 | 16 | // Rlimit specifies the resource limits, such as max open files. 17 | type Rlimit struct { 18 | Type int `json:"type,omitempty"` 19 | Hard uint64 `json:"hard,omitempty"` 20 | Soft uint64 `json:"soft,omitempty"` 21 | } 22 | 23 | const ( 24 | // magic numbers for making the syscall 25 | // some of these are defined in the syscall package, but not all. 26 | // Also since Windows client doesn't get access to the syscall package, need to 27 | // define these here 28 | rlimitAs = 9 29 | rlimitCore = 4 30 | rlimitCPU = 0 31 | rlimitData = 2 32 | rlimitFsize = 1 33 | rlimitLocks = 10 34 | rlimitMemlock = 8 35 | rlimitMsgqueue = 12 36 | rlimitNice = 13 37 | rlimitNofile = 7 38 | rlimitNproc = 6 39 | rlimitRss = 5 40 | rlimitRtprio = 14 41 | rlimitRttime = 15 42 | rlimitSigpending = 11 43 | rlimitStack = 3 44 | ) 45 | 46 | var ulimitNameMapping = map[string]int{ 47 | //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. 48 | "core": rlimitCore, 49 | "cpu": rlimitCPU, 50 | "data": rlimitData, 51 | "fsize": rlimitFsize, 52 | "locks": rlimitLocks, 53 | "memlock": rlimitMemlock, 54 | "msgqueue": rlimitMsgqueue, 55 | "nice": rlimitNice, 56 | "nofile": rlimitNofile, 57 | "nproc": rlimitNproc, 58 | "rss": rlimitRss, 59 | "rtprio": rlimitRtprio, 60 | "rttime": rlimitRttime, 61 | "sigpending": rlimitSigpending, 62 | "stack": rlimitStack, 63 | } 64 | 65 | // ParseUlimit parses and returns a Ulimit from the specified string. 66 | func ParseUlimit(val string) (*Ulimit, error) { 67 | parts := strings.SplitN(val, "=", 2) 68 | if len(parts) != 2 { 69 | return nil, fmt.Errorf("invalid ulimit argument: %s", val) 70 | } 71 | 72 | if _, exists := ulimitNameMapping[parts[0]]; !exists { 73 | return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) 74 | } 75 | 76 | var ( 77 | soft int64 78 | hard = &soft // default to soft in case no hard was set 79 | temp int64 80 | err error 81 | ) 82 | switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { 83 | case 2: 84 | temp, err = strconv.ParseInt(limitVals[1], 10, 64) 85 | if err != nil { 86 | return nil, err 87 | } 88 | hard = &temp 89 | fallthrough 90 | case 1: 91 | soft, err = strconv.ParseInt(limitVals[0], 10, 64) 92 | if err != nil { 93 | return nil, err 94 | } 95 | default: 96 | return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) 97 | } 98 | 99 | if soft > *hard { 100 | return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) 101 | } 102 | 103 | return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil 104 | } 105 | 106 | // GetRlimit returns the RLimit corresponding to Ulimit. 107 | func (u *Ulimit) GetRlimit() (*Rlimit, error) { 108 | t, exists := ulimitNameMapping[u.Name] 109 | if !exists { 110 | return nil, fmt.Errorf("invalid ulimit name %s", u.Name) 111 | } 112 | 113 | return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil 114 | } 115 | 116 | func (u *Ulimit) String() string { 117 | return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) 118 | } 119 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/MAINTAINERS: -------------------------------------------------------------------------------- 1 | Derek McGowan (github: dmcgowan) 2 | Eric Windisch (github: ewindisch) 3 | Josh Hawn (github: jlhawn) 4 | Vincent Batts (github: vbatts) 5 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/README.md: -------------------------------------------------------------------------------- 1 | # tarsum 2 | 3 | Docs: http://godoc.org/github.com/jlhawn/tarsum 4 | 5 | Resumable Tarsum Digest which also implements the `hash.Hash` interface. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/archive/tar/stat_atim.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux dragonfly openbsd solaris 6 | 7 | package tar 8 | 9 | import ( 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | func statAtime(st *syscall.Stat_t) time.Time { 15 | return time.Unix(st.Atim.Unix()) 16 | } 17 | 18 | func statCtime(st *syscall.Stat_t) time.Time { 19 | return time.Unix(st.Ctim.Unix()) 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/archive/tar/stat_atimespec.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin freebsd netbsd 6 | 7 | package tar 8 | 9 | import ( 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | func statAtime(st *syscall.Stat_t) time.Time { 15 | return time.Unix(st.Atimespec.Unix()) 16 | } 17 | 18 | func statCtime(st *syscall.Stat_t) time.Time { 19 | return time.Unix(st.Ctimespec.Unix()) 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/archive/tar/stat_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux darwin dragonfly freebsd openbsd netbsd solaris 6 | 7 | package tar 8 | 9 | import ( 10 | "os" 11 | "syscall" 12 | ) 13 | 14 | func init() { 15 | sysStat = statUnix 16 | } 17 | 18 | func statUnix(fi os.FileInfo, h *Header) error { 19 | sys, ok := fi.Sys().(*syscall.Stat_t) 20 | if !ok { 21 | return nil 22 | } 23 | h.Uid = int(sys.Uid) 24 | h.Gid = int(sys.Gid) 25 | // TODO(bradfitz): populate username & group. os/user 26 | // doesn't cache LookupId lookups, and lacks group 27 | // lookup functions. 28 | h.AccessTime = statAtime(sys) 29 | h.ChangeTime = statCtime(sys) 30 | // TODO(bradfitz): major/minor device numbers? 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/fileinfosums.go: -------------------------------------------------------------------------------- 1 | package tarsum 2 | 3 | import "sort" 4 | 5 | // This info will be accessed through interface so the actual name and sum cannot be medled with 6 | type fileInfoSumInterface interface { 7 | // File name 8 | Name() string 9 | // Checksum of this particular file and its headers 10 | Sum() string 11 | // Position of file in the tar 12 | Pos() int64 13 | } 14 | 15 | type fileInfoSum struct { 16 | name string 17 | sum string 18 | pos int64 19 | } 20 | 21 | func (fis fileInfoSum) Name() string { 22 | return fis.name 23 | } 24 | func (fis fileInfoSum) Sum() string { 25 | return fis.sum 26 | } 27 | func (fis fileInfoSum) Pos() int64 { 28 | return fis.pos 29 | } 30 | 31 | type fileInfoSums []fileInfoSumInterface 32 | 33 | // GetFile returns the first FileInfoSumInterface with a matching name 34 | func (fis fileInfoSums) GetFile(name string) fileInfoSumInterface { 35 | for i := range fis { 36 | if fis[i].Name() == name { 37 | return fis[i] 38 | } 39 | } 40 | return nil 41 | } 42 | 43 | // GetAllFile returns a FileInfoSums with all matching names 44 | func (fis fileInfoSums) GetAllFile(name string) fileInfoSums { 45 | f := fileInfoSums{} 46 | for i := range fis { 47 | if fis[i].Name() == name { 48 | f = append(f, fis[i]) 49 | } 50 | } 51 | return f 52 | } 53 | 54 | func contains(s []string, e string) bool { 55 | for _, a := range s { 56 | if a == e { 57 | return true 58 | } 59 | } 60 | return false 61 | } 62 | 63 | func (fis fileInfoSums) GetDuplicatePaths() (dups fileInfoSums) { 64 | seen := make(map[string]int, len(fis)) // allocate earl. no need to grow this map. 65 | for i := range fis { 66 | f := fis[i] 67 | if _, ok := seen[f.Name()]; ok { 68 | dups = append(dups, f) 69 | } else { 70 | seen[f.Name()] = 0 71 | } 72 | } 73 | return dups 74 | } 75 | 76 | func (fis fileInfoSums) Len() int { return len(fis) } 77 | func (fis fileInfoSums) Swap(i, j int) { fis[i], fis[j] = fis[j], fis[i] } 78 | 79 | func (fis fileInfoSums) SortByPos() { 80 | sort.Sort(byPos{fis}) 81 | } 82 | 83 | func (fis fileInfoSums) SortByNames() { 84 | sort.Sort(byName{fis}) 85 | } 86 | 87 | func (fis fileInfoSums) SortBySums() { 88 | dups := fis.GetDuplicatePaths() 89 | if len(dups) > 0 { 90 | sort.Sort(bySum{fis, dups}) 91 | } else { 92 | sort.Sort(bySum{fis, nil}) 93 | } 94 | } 95 | 96 | // byName is a sort.Sort helper for sorting by file names. 97 | // If names are the same, order them by their appearance in the tar archive 98 | type byName struct{ fileInfoSums } 99 | 100 | func (bn byName) Less(i, j int) bool { 101 | if bn.fileInfoSums[i].Name() == bn.fileInfoSums[j].Name() { 102 | return bn.fileInfoSums[i].Pos() < bn.fileInfoSums[j].Pos() 103 | } 104 | return bn.fileInfoSums[i].Name() < bn.fileInfoSums[j].Name() 105 | } 106 | 107 | // bySum is a sort.Sort helper for sorting by the sums of all the fileinfos in the tar archive 108 | type bySum struct { 109 | fileInfoSums 110 | dups fileInfoSums 111 | } 112 | 113 | func (bs bySum) Less(i, j int) bool { 114 | if bs.dups != nil && bs.fileInfoSums[i].Name() == bs.fileInfoSums[j].Name() { 115 | return bs.fileInfoSums[i].Pos() < bs.fileInfoSums[j].Pos() 116 | } 117 | return bs.fileInfoSums[i].Sum() < bs.fileInfoSums[j].Sum() 118 | } 119 | 120 | // byPos is a sort.Sort helper for sorting by the sums of all the fileinfos by their original order 121 | type byPos struct{ fileInfoSums } 122 | 123 | func (bp byPos) Less(i, j int) bool { 124 | return bp.fileInfoSums[i].Pos() < bp.fileInfoSums[j].Pos() 125 | } 126 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/fileinfosums_test.go: -------------------------------------------------------------------------------- 1 | package tarsum 2 | 3 | import "testing" 4 | 5 | func newFileInfoSums() fileInfoSums { 6 | return fileInfoSums{ 7 | fileInfoSum{name: "file3", sum: "2abcdef1234567890", pos: 2}, 8 | fileInfoSum{name: "dup1", sum: "deadbeef1", pos: 5}, 9 | fileInfoSum{name: "file1", sum: "0abcdef1234567890", pos: 0}, 10 | fileInfoSum{name: "file4", sum: "3abcdef1234567890", pos: 3}, 11 | fileInfoSum{name: "dup1", sum: "deadbeef0", pos: 4}, 12 | fileInfoSum{name: "file2", sum: "1abcdef1234567890", pos: 1}, 13 | } 14 | } 15 | 16 | func TestSortFileInfoSums(t *testing.T) { 17 | dups := newFileInfoSums().GetAllFile("dup1") 18 | if len(dups) != 2 { 19 | t.Errorf("expected length 2, got %d", len(dups)) 20 | } 21 | dups.SortByNames() 22 | if dups[0].Pos() != 4 { 23 | t.Errorf("sorted dups should be ordered by position. Expected 4, got %d", dups[0].Pos()) 24 | } 25 | 26 | fis := newFileInfoSums() 27 | expected := "0abcdef1234567890" 28 | fis.SortBySums() 29 | got := fis[0].Sum() 30 | if got != expected { 31 | t.Errorf("Expected %q, got %q", expected, got) 32 | } 33 | 34 | fis = newFileInfoSums() 35 | expected = "dup1" 36 | fis.SortByNames() 37 | gotFis := fis[0] 38 | if gotFis.Name() != expected { 39 | t.Errorf("Expected %q, got %q", expected, gotFis.Name()) 40 | } 41 | // since a duplicate is first, ensure it is ordered first by position too 42 | if gotFis.Pos() != 4 { 43 | t.Errorf("Expected %d, got %d", 4, gotFis.Pos()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/sha256/sha256block.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !386,!amd64 6 | 7 | // SHA256 block step. 8 | // In its own file so that a faster assembly or C version 9 | // can be substituted easily. 10 | 11 | package sha256 12 | 13 | var _K = []uint32{ 14 | 0x428a2f98, 15 | 0x71374491, 16 | 0xb5c0fbcf, 17 | 0xe9b5dba5, 18 | 0x3956c25b, 19 | 0x59f111f1, 20 | 0x923f82a4, 21 | 0xab1c5ed5, 22 | 0xd807aa98, 23 | 0x12835b01, 24 | 0x243185be, 25 | 0x550c7dc3, 26 | 0x72be5d74, 27 | 0x80deb1fe, 28 | 0x9bdc06a7, 29 | 0xc19bf174, 30 | 0xe49b69c1, 31 | 0xefbe4786, 32 | 0x0fc19dc6, 33 | 0x240ca1cc, 34 | 0x2de92c6f, 35 | 0x4a7484aa, 36 | 0x5cb0a9dc, 37 | 0x76f988da, 38 | 0x983e5152, 39 | 0xa831c66d, 40 | 0xb00327c8, 41 | 0xbf597fc7, 42 | 0xc6e00bf3, 43 | 0xd5a79147, 44 | 0x06ca6351, 45 | 0x14292967, 46 | 0x27b70a85, 47 | 0x2e1b2138, 48 | 0x4d2c6dfc, 49 | 0x53380d13, 50 | 0x650a7354, 51 | 0x766a0abb, 52 | 0x81c2c92e, 53 | 0x92722c85, 54 | 0xa2bfe8a1, 55 | 0xa81a664b, 56 | 0xc24b8b70, 57 | 0xc76c51a3, 58 | 0xd192e819, 59 | 0xd6990624, 60 | 0xf40e3585, 61 | 0x106aa070, 62 | 0x19a4c116, 63 | 0x1e376c08, 64 | 0x2748774c, 65 | 0x34b0bcb5, 66 | 0x391c0cb3, 67 | 0x4ed8aa4a, 68 | 0x5b9cca4f, 69 | 0x682e6ff3, 70 | 0x748f82ee, 71 | 0x78a5636f, 72 | 0x84c87814, 73 | 0x8cc70208, 74 | 0x90befffa, 75 | 0xa4506ceb, 76 | 0xbef9a3f7, 77 | 0xc67178f2, 78 | } 79 | 80 | func block(dig *digest, p []byte) { 81 | var w [64]uint32 82 | h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] 83 | for len(p) >= chunk { 84 | // Can interlace the computation of w with the 85 | // rounds below if needed for speed. 86 | for i := 0; i < 16; i++ { 87 | j := i * 4 88 | w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) 89 | } 90 | for i := 16; i < 64; i++ { 91 | v1 := w[i-2] 92 | t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10) 93 | v2 := w[i-15] 94 | t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3) 95 | w[i] = t1 + w[i-7] + t2 + w[i-16] 96 | } 97 | 98 | a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7 99 | 100 | for i := 0; i < 64; i++ { 101 | t1 := h + ((e>>6 | e<<(32-6)) ^ (e>>11 | e<<(32-11)) ^ (e>>25 | e<<(32-25))) + ((e & f) ^ (^e & g)) + _K[i] + w[i] 102 | 103 | t2 := ((a>>2 | a<<(32-2)) ^ (a>>13 | a<<(32-13)) ^ (a>>22 | a<<(32-22))) + ((a & b) ^ (a & c) ^ (b & c)) 104 | 105 | h = g 106 | g = f 107 | f = e 108 | e = d + t1 109 | d = c 110 | c = b 111 | b = a 112 | a = t1 + t2 113 | } 114 | 115 | h0 += a 116 | h1 += b 117 | h2 += c 118 | h3 += d 119 | h4 += e 120 | h5 += f 121 | h6 += g 122 | h7 += h 123 | 124 | p = p[chunk:] 125 | } 126 | 127 | dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 128 | } 129 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/sha256/sha256block_decl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build 386 amd64 6 | 7 | package sha256 8 | 9 | //go:noescape 10 | 11 | func block(dig *digest, p []byte) 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/json: -------------------------------------------------------------------------------- 1 | {"id":"46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457","parent":"def3f9165934325dfd027c86530b2ea49bb57a0963eb1336b3a0415ff6fd56de","created":"2014-04-07T02:45:52.610504484Z","container":"e0f07f8d72cae171a3dcc35859960e7e956e0628bce6fedc4122bf55b2c287c7","container_config":{"Hostname":"88807319f25e","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["HOME=/","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","sed -ri 's/^(%wheel.*)(ALL)$/\\1NOPASSWD: \\2/' /etc/sudoers"],"Image":"def3f9165934325dfd027c86530b2ea49bb57a0963eb1336b3a0415ff6fd56de","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":[]},"docker_version":"0.9.1-dev","config":{"Hostname":"88807319f25e","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["HOME=/","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":null,"Image":"def3f9165934325dfd027c86530b2ea49bb57a0963eb1336b3a0415ff6fd56de","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":[]},"architecture":"amd64","os":"linux","Size":3425} -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/testdata/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json: -------------------------------------------------------------------------------- 1 | {"id":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158","comment":"Imported from -","created":"2013-06-13T14:03:50.821769-07:00","container_config":{"Hostname":"","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":null},"docker_version":"0.4.0","architecture":"x86_64","Size":0} -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/testdata/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar: -------------------------------------------------------------------------------- 1 | ./0040755000000000000000000000000012156431635007413 5ustar0000000000000000 -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/testdata/xattr/json: -------------------------------------------------------------------------------- 1 | {"id":"4439c3c7f847954100b42b267e7e5529cac1d6934db082f65795c5ca2e594d93","parent":"73b164f4437db87e96e90083c73a6592f549646ae2ec00ed33c6b9b49a5c4470","created":"2014-05-16T17:19:44.091534414Z","container":"5f92fb06cc58f357f0cde41394e2bbbb664e663974b2ac1693ab07b7a306749b","container_config":{"Hostname":"9565c6517a0e","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["HOME=/","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","setcap 'cap_setgid,cap_setuid+ep' ./file \u0026\u0026 getcap ./file"],"Image":"73b164f4437db87e96e90083c73a6592f549646ae2ec00ed33c6b9b49a5c4470","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":[]},"docker_version":"0.11.1-dev","config":{"Hostname":"9565c6517a0e","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["HOME=/","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":null,"Image":"73b164f4437db87e96e90083c73a6592f549646ae2ec00ed33c6b9b49a5c4470","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":[]},"architecture":"amd64","os":"linux","Size":0} -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/testdata/xattr/layer.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlhawn/dockramp/fa026f9d0ef7a388cc8487e601adc3ab8d508551/Godeps/_workspace/src/github.com/jlhawn/tarsum/testdata/xattr/layer.tar -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/versioning.go: -------------------------------------------------------------------------------- 1 | package tarsum 2 | 3 | import ( 4 | "errors" 5 | "sort" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/jlhawn/tarsum/archive/tar" 10 | ) 11 | 12 | // versioning of the TarSum algorithm 13 | // based on the prefix of the hash used 14 | // i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b" 15 | type Version int 16 | 17 | // Prefix of "tarsum" 18 | const ( 19 | Version0 Version = iota 20 | Version1 21 | // NOTE: this variable will be either the latest or an unsettled next-version of the TarSum calculation 22 | VersionDev 23 | ) 24 | 25 | // Get a list of all known tarsum Version 26 | func GetVersions() []Version { 27 | v := []Version{} 28 | for k := range tarSumVersions { 29 | v = append(v, k) 30 | } 31 | return v 32 | } 33 | 34 | var tarSumVersions = map[Version]string{ 35 | Version0: "tarsum", 36 | Version1: "tarsum.v1", 37 | VersionDev: "tarsum.dev", 38 | } 39 | 40 | func (tsv Version) String() string { 41 | return tarSumVersions[tsv] 42 | } 43 | 44 | // GetVersionFromTarsum returns the Version from the provided string 45 | func GetVersionFromTarsum(tarsum string) (Version, error) { 46 | tsv := tarsum 47 | if strings.Contains(tarsum, "+") { 48 | tsv = strings.SplitN(tarsum, "+", 2)[0] 49 | } 50 | for v, s := range tarSumVersions { 51 | if s == tsv { 52 | return v, nil 53 | } 54 | } 55 | return -1, ErrNotVersion 56 | } 57 | 58 | // Errors that may be returned by functions in this package 59 | var ( 60 | ErrNotVersion = errors.New("string does not include a TarSum Version") 61 | ErrVersionNotImplemented = errors.New("TarSum Version is not yet implemented") 62 | ) 63 | 64 | // tarHeaderSelector is the interface which different versions 65 | // of tarsum should use for selecting and ordering tar headers 66 | // for each item in the archive. 67 | type tarHeaderSelector interface { 68 | selectHeaders(h *tar.Header) (orderedHeaders [][2]string) 69 | } 70 | 71 | type tarHeaderSelectFunc func(h *tar.Header) (orderedHeaders [][2]string) 72 | 73 | func (f tarHeaderSelectFunc) selectHeaders(h *tar.Header) (orderedHeaders [][2]string) { 74 | return f(h) 75 | } 76 | 77 | func v0TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) { 78 | return [][2]string{ 79 | {"name", h.Name}, 80 | {"mode", strconv.Itoa(int(h.Mode))}, 81 | {"uid", strconv.Itoa(h.Uid)}, 82 | {"gid", strconv.Itoa(h.Gid)}, 83 | {"size", strconv.Itoa(int(h.Size))}, 84 | {"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))}, 85 | {"typeflag", string([]byte{h.Typeflag})}, 86 | {"linkname", h.Linkname}, 87 | {"uname", h.Uname}, 88 | {"gname", h.Gname}, 89 | {"devmajor", strconv.Itoa(int(h.Devmajor))}, 90 | {"devminor", strconv.Itoa(int(h.Devminor))}, 91 | } 92 | } 93 | 94 | func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) { 95 | // Get extended attributes. 96 | xAttrKeys := make([]string, len(h.Xattrs)) 97 | for k := range h.Xattrs { 98 | xAttrKeys = append(xAttrKeys, k) 99 | } 100 | sort.Strings(xAttrKeys) 101 | 102 | // Make the slice with enough capacity to hold the 11 basic headers 103 | // we want from the v0 selector plus however many xattrs we have. 104 | orderedHeaders = make([][2]string, 0, 11+len(xAttrKeys)) 105 | 106 | // Copy all headers from v0 excluding the 'mtime' header (the 5th element). 107 | v0headers := v0TarHeaderSelect(h) 108 | orderedHeaders = append(orderedHeaders, v0headers[0:5]...) 109 | orderedHeaders = append(orderedHeaders, v0headers[6:]...) 110 | 111 | // Finally, append the sorted xattrs. 112 | for _, k := range xAttrKeys { 113 | orderedHeaders = append(orderedHeaders, [2]string{k, h.Xattrs[k]}) 114 | } 115 | 116 | return 117 | } 118 | 119 | var registeredHeaderSelectors = map[Version]tarHeaderSelectFunc{ 120 | Version0: v0TarHeaderSelect, 121 | Version1: v1TarHeaderSelect, 122 | VersionDev: v1TarHeaderSelect, 123 | } 124 | 125 | func getTarHeaderSelector(v Version) (tarHeaderSelector, error) { 126 | headerSelector, ok := registeredHeaderSelectors[v] 127 | if !ok { 128 | return nil, ErrVersionNotImplemented 129 | } 130 | 131 | return headerSelector, nil 132 | } 133 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/versioning_test.go: -------------------------------------------------------------------------------- 1 | package tarsum 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestVersion(t *testing.T) { 8 | expected := "tarsum" 9 | var v Version 10 | if v.String() != expected { 11 | t.Errorf("expected %q, got %q", expected, v.String()) 12 | } 13 | 14 | expected = "tarsum.v1" 15 | v = 1 16 | if v.String() != expected { 17 | t.Errorf("expected %q, got %q", expected, v.String()) 18 | } 19 | 20 | expected = "tarsum.dev" 21 | v = 2 22 | if v.String() != expected { 23 | t.Errorf("expected %q, got %q", expected, v.String()) 24 | } 25 | } 26 | 27 | func TestGetVersion(t *testing.T) { 28 | testSet := []struct { 29 | Str string 30 | Expected Version 31 | }{ 32 | {"tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b", Version0}, 33 | {"tarsum+sha256", Version0}, 34 | {"tarsum", Version0}, 35 | {"tarsum.dev", VersionDev}, 36 | {"tarsum.dev+sha256:deadbeef", VersionDev}, 37 | } 38 | 39 | for _, ts := range testSet { 40 | v, err := GetVersionFromTarsum(ts.Str) 41 | if err != nil { 42 | t.Fatalf("%q : %s", err, ts.Str) 43 | } 44 | if v != ts.Expected { 45 | t.Errorf("expected %d (%q), got %d (%q)", ts.Expected, ts.Expected, v, v) 46 | } 47 | } 48 | 49 | // test one that does not exist, to ensure it errors 50 | str := "weak+md5:abcdeabcde" 51 | _, err := GetVersionFromTarsum(str) 52 | if err != ErrNotVersion { 53 | t.Fatalf("%q : %s", err, str) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jlhawn/tarsum/writercloser.go: -------------------------------------------------------------------------------- 1 | package tarsum 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type writeCloseFlusher interface { 8 | io.WriteCloser 9 | Flush() error 10 | } 11 | 12 | type nopCloseFlusher struct { 13 | io.Writer 14 | } 15 | 16 | func (n *nopCloseFlusher) Close() error { 17 | return nil 18 | } 19 | 20 | func (n *nopCloseFlusher) Flush() error { 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/samalba/dockerclient/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/samalba/dockerclient/README.md: -------------------------------------------------------------------------------- 1 | Docker client library in Go 2 | =========================== 3 | [![GoDoc](http://godoc.org/github.com/samalba/dockerclient?status.png)](http://godoc.org/github.com/samalba/dockerclient) 4 | 5 | Well maintained docker client library. 6 | 7 | # How to use it? 8 | 9 | Here is an example showing how to use it: 10 | 11 | ```go 12 | package main 13 | 14 | import ( 15 | "github.com/samalba/dockerclient" 16 | "log" 17 | "time" 18 | "os" 19 | ) 20 | 21 | // Callback used to listen to Docker's events 22 | func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) { 23 | log.Printf("Received event: %#v\n", *event) 24 | } 25 | 26 | func main() { 27 | // Init the client 28 | docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil) 29 | 30 | // Get only running containers 31 | containers, err := docker.ListContainers(false, false, "") 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | for _, c := range containers { 36 | log.Println(c.Id, c.Names) 37 | } 38 | 39 | // Inspect the first container returned 40 | if len(containers) > 0 { 41 | id := containers[0].Id 42 | info, _ := docker.InspectContainer(id) 43 | log.Println(info) 44 | } 45 | 46 | // Build a docker image 47 | // some.tar contains the build context (Dockerfile any any files it needs to add/copy) 48 | dockerBuildContext, err := os.Open("some.tar") 49 | defer dockerBuildContext.Close() 50 | buildImageConfig := &dockerclient.BuildImage{ 51 | Context: dockerBuildContext, 52 | RepoName: "your_image_name", 53 | SuppressOutput: false, 54 | } 55 | reader, err := docker.BuildImage(buildImageConfig) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | // Create a container 61 | containerConfig := &dockerclient.ContainerConfig{ 62 | Image: "ubuntu:14.04", 63 | Cmd: []string{"bash"}, 64 | AttachStdin: true, 65 | Tty: true} 66 | containerId, err := docker.CreateContainer(containerConfig, "foobar", nil) 67 | if err != nil { 68 | log.Fatal(err) 69 | } 70 | 71 | // Start the container 72 | hostConfig := &dockerclient.HostConfig{} 73 | err = docker.StartContainer(containerId, hostConfig) 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | 78 | // Stop the container (with 5 seconds timeout) 79 | docker.StopContainer(containerId, 5) 80 | 81 | // Listen to events 82 | docker.StartMonitorEvents(eventCallback, nil) 83 | 84 | // Hold the execution to look at the events coming 85 | time.Sleep(3600 * time.Second) 86 | } 87 | ``` 88 | 89 | # Maintainers 90 | 91 | List of people you can ping for feedback on Pull Requests or any questions. 92 | 93 | - [Sam Alba](https://github.com/samalba) 94 | - [Michael Crosby](https://github.com/crosbymichael) 95 | - [Andrea Luzzardi](https://github.com/aluzzardi) 96 | - [Victor Vieux](https://github.com/vieux) 97 | - [Evan Hazlett](https://github.com/ehazlett) 98 | - [Donald Huang](https://github.com/donhcd) 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/samalba/dockerclient/auth.go: -------------------------------------------------------------------------------- 1 | package dockerclient 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | ) 8 | 9 | // AuthConfig hold parameters for authenticating with the docker registry 10 | type AuthConfig struct { 11 | Username string `json:"username,omitempty"` 12 | Password string `json:"password,omitempty"` 13 | Email string `json:"email,omitempty"` 14 | RegistryToken string `json:"registrytoken,omitempty"` 15 | } 16 | 17 | // encode the auth configuration struct into base64 for the X-Registry-Auth header 18 | func (c *AuthConfig) encode() (string, error) { 19 | var buf bytes.Buffer 20 | if err := json.NewEncoder(&buf).Encode(c); err != nil { 21 | return "", err 22 | } 23 | return base64.URLEncoding.EncodeToString(buf.Bytes()), nil 24 | } 25 | 26 | // ConfigFile holds parameters for authenticating during a BuildImage request 27 | type ConfigFile struct { 28 | Configs map[string]AuthConfig `json:"configs,omitempty"` 29 | rootPath string 30 | } 31 | 32 | // encode the configuration struct into base64 for the X-Registry-Config header 33 | func (c *ConfigFile) encode() (string, error) { 34 | var buf bytes.Buffer 35 | if err := json.NewEncoder(&buf).Encode(c); err != nil { 36 | return "", err 37 | } 38 | return base64.URLEncoding.EncodeToString(buf.Bytes()), nil 39 | } 40 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go: -------------------------------------------------------------------------------- 1 | package dockerclient 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type Callback func(*Event, chan error, ...interface{}) 8 | 9 | type StatCallback func(string, *Stats, chan error, ...interface{}) 10 | 11 | type Client interface { 12 | Info() (*Info, error) 13 | ListContainers(all, size bool, filters string) ([]Container, error) 14 | InspectContainer(id string) (*ContainerInfo, error) 15 | InspectImage(id string) (*ImageInfo, error) 16 | CreateContainer(config *ContainerConfig, name string, authConfig *AuthConfig) (string, error) 17 | ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) 18 | ContainerChanges(id string) ([]*ContainerChanges, error) 19 | // ContainerStats takes a container ID and an optional stop channel and 20 | // returns a StatsOrError channel. If an error is ever sent, then no 21 | // more stats will be sent on that channel. If a stop channel is 22 | // provided, events will stop being monitored after the stop channel is 23 | // closed. 24 | ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error) 25 | ExecCreate(config *ExecConfig) (string, error) 26 | ExecStart(id string, config *ExecConfig) error 27 | ExecResize(id string, width, height int) error 28 | StartContainer(id string, config *HostConfig) error 29 | AttachContainer(id string, options *AttachOptions) (io.ReadCloser, error) 30 | StopContainer(id string, timeout int) error 31 | RestartContainer(id string, timeout int) error 32 | KillContainer(id, signal string) error 33 | Wait(id string) <-chan WaitResult 34 | // MonitorEvents takes options and an optional stop channel, and returns 35 | // an EventOrError channel. If an error is ever sent, then no more 36 | // events will be sent. If a stop channel is provided, events will stop 37 | // being monitored after the stop channel is closed. 38 | MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) 39 | StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) 40 | StopAllMonitorEvents() 41 | StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) 42 | StopAllMonitorStats() 43 | TagImage(nameOrID string, repo string, tag string, force bool) error 44 | Version() (*Version, error) 45 | PullImage(name string, auth *AuthConfig) error 46 | PushImage(name string, tag string, auth *AuthConfig) error 47 | LoadImage(reader io.Reader) error 48 | RemoveContainer(id string, force, volumes bool) error 49 | ListImages(all bool) ([]*Image, error) 50 | RemoveImage(name string, force bool) ([]*ImageDelete, error) 51 | SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error) 52 | PauseContainer(name string) error 53 | UnpauseContainer(name string) error 54 | RenameContainer(oldName string, newName string) error 55 | ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) 56 | BuildImage(image *BuildImage) (io.ReadCloser, error) 57 | ListVolumes() ([]*Volume, error) 58 | RemoveVolume(name string) error 59 | CreateVolume(request *VolumeCreateRequest) (*Volume, error) 60 | ListNetworks(filters string) ([]*NetworkResource, error) 61 | InspectNetwork(id string) (*NetworkResource, error) 62 | CreateNetwork(config *NetworkCreate) (*NetworkCreateResponse, error) 63 | ConnectNetwork(id, container string) error 64 | DisconnectNetwork(id, container string, force bool) error 65 | RemoveNetwork(id string) error 66 | } 67 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/samalba/dockerclient/tls.go: -------------------------------------------------------------------------------- 1 | package dockerclient 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "errors" 7 | "io/ioutil" 8 | "path/filepath" 9 | ) 10 | 11 | // TLSConfigFromCertPath returns a configuration based on PEM files in the directory 12 | // 13 | // path is usually what is set by the environment variable `DOCKER_CERT_PATH`, 14 | // or `$HOME/.docker`. 15 | func TLSConfigFromCertPath(path string) (*tls.Config, error) { 16 | cert, err := ioutil.ReadFile(filepath.Join(path, "cert.pem")) 17 | if err != nil { 18 | return nil, err 19 | } 20 | key, err := ioutil.ReadFile(filepath.Join(path, "key.pem")) 21 | if err != nil { 22 | return nil, err 23 | } 24 | ca, err := ioutil.ReadFile(filepath.Join(path, "ca.pem")) 25 | if err != nil { 26 | return nil, err 27 | } 28 | tlsCert, err := tls.X509KeyPair(cert, key) 29 | if err != nil { 30 | return nil, err 31 | } 32 | tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}} 33 | tlsConfig.RootCAs = x509.NewCertPool() 34 | if !tlsConfig.RootCAs.AppendCertsFromPEM(ca) { 35 | return nil, errors.New("Could not add RootCA pem") 36 | } 37 | return tlsConfig, nil 38 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/samalba/dockerclient/utils.go: -------------------------------------------------------------------------------- 1 | package dockerclient 2 | 3 | import ( 4 | "crypto/tls" 5 | "net" 6 | "net/http" 7 | "net/url" 8 | "time" 9 | ) 10 | 11 | type tcpFunc func(*net.TCPConn, time.Duration) error 12 | 13 | func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration, setUserTimeout tcpFunc) *http.Client { 14 | httpTransport := &http.Transport{ 15 | TLSClientConfig: tlsConfig, 16 | } 17 | 18 | switch u.Scheme { 19 | default: 20 | httpTransport.Dial = func(proto, addr string) (net.Conn, error) { 21 | conn, err := net.DialTimeout(proto, addr, timeout) 22 | if tcpConn, ok := conn.(*net.TCPConn); ok && setUserTimeout != nil { 23 | // Sender can break TCP connection if the remote side doesn't 24 | // acknowledge packets within timeout 25 | setUserTimeout(tcpConn, timeout) 26 | } 27 | return conn, err 28 | } 29 | case "unix": 30 | socketPath := u.Path 31 | unixDial := func(proto, addr string) (net.Conn, error) { 32 | return net.DialTimeout("unix", socketPath, timeout) 33 | } 34 | httpTransport.Dial = unixDial 35 | // Override the main URL object so the HTTP lib won't complain 36 | u.Scheme = "http" 37 | u.Host = "unix.sock" 38 | u.Path = "" 39 | } 40 | return &http.Client{Transport: httpTransport} 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/net/context/go17.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.7 6 | 7 | package context 8 | 9 | import ( 10 | "context" // standard library's context, as of Go 1.7 11 | "time" 12 | ) 13 | 14 | var ( 15 | todo = context.TODO() 16 | background = context.Background() 17 | ) 18 | 19 | // Canceled is the error returned by Context.Err when the context is canceled. 20 | var Canceled = context.Canceled 21 | 22 | // DeadlineExceeded is the error returned by Context.Err when the context's 23 | // deadline passes. 24 | var DeadlineExceeded = context.DeadlineExceeded 25 | 26 | // WithCancel returns a copy of parent with a new Done channel. The returned 27 | // context's Done channel is closed when the returned cancel function is called 28 | // or when the parent context's Done channel is closed, whichever happens first. 29 | // 30 | // Canceling this context releases resources associated with it, so code should 31 | // call cancel as soon as the operations running in this Context complete. 32 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { 33 | ctx, f := context.WithCancel(parent) 34 | return ctx, CancelFunc(f) 35 | } 36 | 37 | // WithDeadline returns a copy of the parent context with the deadline adjusted 38 | // to be no later than d. If the parent's deadline is already earlier than d, 39 | // WithDeadline(parent, d) is semantically equivalent to parent. The returned 40 | // context's Done channel is closed when the deadline expires, when the returned 41 | // cancel function is called, or when the parent context's Done channel is 42 | // closed, whichever happens first. 43 | // 44 | // Canceling this context releases resources associated with it, so code should 45 | // call cancel as soon as the operations running in this Context complete. 46 | func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { 47 | ctx, f := context.WithDeadline(parent, deadline) 48 | return ctx, CancelFunc(f) 49 | } 50 | 51 | // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). 52 | // 53 | // Canceling this context releases resources associated with it, so code should 54 | // call cancel as soon as the operations running in this Context complete: 55 | // 56 | // func slowOperationWithTimeout(ctx context.Context) (Result, error) { 57 | // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) 58 | // defer cancel() // releases resources if slowOperation completes before timeout elapses 59 | // return slowOperation(ctx) 60 | // } 61 | func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { 62 | return WithDeadline(parent, time.Now().Add(timeout)) 63 | } 64 | 65 | // WithValue returns a copy of parent in which the value associated with key is 66 | // val. 67 | // 68 | // Use context Values only for request-scoped data that transits processes and 69 | // APIs, not for passing optional parameters to functions. 70 | func WithValue(parent Context, key interface{}, val interface{}) Context { 71 | return context.WithValue(parent, key, val) 72 | } 73 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Set an output prefix, which is the local directory if not specified 2 | GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) 3 | 4 | .PHONY: all clean image binaries 5 | 6 | all: clean binaries 7 | 8 | clean: 9 | @echo "+ $@" 10 | @rm -rf bundles 11 | 12 | image: 13 | @echo "+ $@" 14 | @dockramp -t dockramp:${GIT_BRANCH} 15 | 16 | binaries: image 17 | @echo "+ $@" 18 | $(eval C_ID := $(shell docker create dockramp:${GIT_BRANCH})) 19 | @docker start -a ${C_ID} 20 | @docker cp ${C_ID}:/bundles . 21 | @docker rm ${C_ID} 22 | -------------------------------------------------------------------------------- /archive/archive_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package archive 4 | 5 | import ( 6 | "archive/tar" 7 | "errors" 8 | "os" 9 | "syscall" 10 | 11 | "github.com/docker/docker/pkg/system" 12 | ) 13 | 14 | // CanonicalTarNameForPath returns platform-specific filepath 15 | // to canonical posix-style path for tar archival. p is relative 16 | // path. 17 | func CanonicalTarNameForPath(p string) (string, error) { 18 | return p, nil // already unix-style 19 | } 20 | 21 | // chmodTarEntry is used to adjust the file permissions used in tar header based 22 | // on the platform the archival is done. 23 | 24 | func chmodTarEntry(perm os.FileMode) os.FileMode { 25 | return perm // noop for unix as golang APIs provide perm bits correctly 26 | } 27 | 28 | func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) { 29 | s, ok := stat.(*syscall.Stat_t) 30 | 31 | if !ok { 32 | err = errors.New("cannot convert stat value to syscall.Stat_t") 33 | return 34 | } 35 | 36 | nlink = uint32(s.Nlink) 37 | inode = uint64(s.Ino) 38 | 39 | // Currently go does not fil in the major/minors 40 | if s.Mode&syscall.S_IFBLK != 0 || 41 | s.Mode&syscall.S_IFCHR != 0 { 42 | hdr.Devmajor = int64(major(uint64(s.Rdev))) 43 | hdr.Devminor = int64(minor(uint64(s.Rdev))) 44 | } 45 | 46 | return 47 | } 48 | 49 | func major(device uint64) uint64 { 50 | return (device >> 8) & 0xfff 51 | } 52 | 53 | func minor(device uint64) uint64 { 54 | return (device & 0xff) | ((device >> 12) & 0xfff00) 55 | } 56 | 57 | // handleTarTypeBlockCharFifo is an OS-specific helper function used by 58 | // createTarFile to handle the following types of header: Block; Char; Fifo 59 | func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { 60 | mode := uint32(hdr.Mode & 07777) 61 | switch hdr.Typeflag { 62 | case tar.TypeBlock: 63 | mode |= syscall.S_IFBLK 64 | case tar.TypeChar: 65 | mode |= syscall.S_IFCHR 66 | case tar.TypeFifo: 67 | mode |= syscall.S_IFIFO 68 | } 69 | 70 | if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil { 71 | return err 72 | } 73 | return nil 74 | } 75 | 76 | func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { 77 | if hdr.Typeflag == tar.TypeLink { 78 | if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { 79 | if err := os.Chmod(path, hdrInfo.Mode()); err != nil { 80 | return err 81 | } 82 | } 83 | } else if hdr.Typeflag != tar.TypeSymlink { 84 | if err := os.Chmod(path, hdrInfo.Mode()); err != nil { 85 | return err 86 | } 87 | } 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /archive/archive_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package archive 4 | 5 | import ( 6 | "archive/tar" 7 | "fmt" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | // canonicalTarNameForPath returns platform-specific filepath 13 | // to canonical posix-style path for tar archival. p is relative 14 | // path. 15 | func CanonicalTarNameForPath(p string) (string, error) { 16 | // windows: convert windows style relative path with backslashes 17 | // into forward slashes. Since windows does not allow '/' or '\' 18 | // in file names, it is mostly safe to replace however we must 19 | // check just in case 20 | if strings.Contains(p, "/") { 21 | return "", fmt.Errorf("Windows path contains forward slash: %s", p) 22 | } 23 | return strings.Replace(p, string(os.PathSeparator), "/", -1), nil 24 | 25 | } 26 | 27 | // chmodTarEntry is used to adjust the file permissions used in tar header based 28 | // on the platform the archival is done. 29 | func chmodTarEntry(perm os.FileMode) os.FileMode { 30 | perm &= 0755 31 | // Add the x bit: make everything +x from windows 32 | perm |= 0111 33 | 34 | return perm 35 | } 36 | 37 | func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) { 38 | // do nothing. no notion of Rdev, Inode, Nlink in stat on Windows 39 | return 40 | } 41 | 42 | // handleTarTypeBlockCharFifo is an OS-specific helper function used by 43 | // createTarFile to handle the following types of header: Block; Char; Fifo 44 | func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { 45 | return nil 46 | } 47 | 48 | func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /archive/testdata/broken.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlhawn/dockramp/fa026f9d0ef7a388cc8487e601adc3ab8d508551/archive/testdata/broken.tar -------------------------------------------------------------------------------- /archive/time_linux.go: -------------------------------------------------------------------------------- 1 | package archive 2 | 3 | import ( 4 | "syscall" 5 | "time" 6 | ) 7 | 8 | func timeToTimespec(time time.Time) (ts syscall.Timespec) { 9 | if time.IsZero() { 10 | // Return UTIME_OMIT special value 11 | ts.Sec = 0 12 | ts.Nsec = ((1 << 30) - 2) 13 | return 14 | } 15 | return syscall.NsecToTimespec(time.UnixNano()) 16 | } 17 | -------------------------------------------------------------------------------- /archive/time_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | package archive 4 | 5 | import ( 6 | "syscall" 7 | "time" 8 | ) 9 | 10 | func timeToTimespec(time time.Time) (ts syscall.Timespec) { 11 | nsec := int64(0) 12 | if !time.IsZero() { 13 | nsec = time.UnixNano() 14 | } 15 | return syscall.NsecToTimespec(nsec) 16 | } 17 | -------------------------------------------------------------------------------- /build/cache.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | "os/user" 9 | "path/filepath" 10 | ) 11 | 12 | func (b *Builder) probeCache() bool { 13 | imageID, cacheHit := b.cache[b.getCacheKey()] 14 | if !cacheHit { 15 | return false 16 | } 17 | 18 | if _, err := b.client.InspectImage(imageID); err != nil { 19 | return false 20 | } 21 | 22 | b.imageID = imageID 23 | b.uncommitted = false 24 | b.uncommittedCommands = nil 25 | 26 | fmt.Fprintf(b.out, " cache hit ---> %s\n", b.imageID) 27 | 28 | return true 29 | } 30 | 31 | func (b *Builder) getCacheKey() string { 32 | hasher := sha256.New() 33 | 34 | // Note: hash.Hash never returns an error. 35 | hasher.Write([]byte(b.imageID)) 36 | 37 | for _, command := range b.uncommittedCommands { 38 | hasher.Write([]byte(command)) 39 | } 40 | 41 | return fmt.Sprintf("%x", hasher.Sum(nil)) 42 | } 43 | 44 | func (b *Builder) setCache(imageID string) error { 45 | b.cache[b.getCacheKey()] = imageID 46 | 47 | return b.saveCache() 48 | } 49 | 50 | func (b *Builder) loadCache() (err error) { 51 | b.cache = map[string]string{} 52 | 53 | usr, err := user.Current() 54 | if err != nil { 55 | return fmt.Errorf("unable to get current user: %s", err) 56 | } 57 | 58 | cacheFilename := fmt.Sprintf("%s%c%s", usr.HomeDir, filepath.Separator, ".dockrampcache") 59 | cacheFile, err := os.Open(cacheFilename) 60 | if os.IsNotExist(err) { 61 | // No cache file exists to load. 62 | return nil 63 | } 64 | if err != nil { 65 | return fmt.Errorf("unable to open cache file: %s", err) 66 | } 67 | defer func() { 68 | if closeErr := cacheFile.Close(); err == nil { 69 | err = closeErr 70 | } 71 | }() 72 | 73 | if err := json.NewDecoder(cacheFile).Decode(&b.cache); err != nil { 74 | return fmt.Errorf("unable to decode build cache: %s", err) 75 | } 76 | 77 | return nil 78 | } 79 | 80 | func (b *Builder) saveCache() (err error) { 81 | usr, err := user.Current() 82 | if err != nil { 83 | return fmt.Errorf("unable to get current user: %s", err) 84 | } 85 | 86 | cacheFilename := fmt.Sprintf("%s%c%s", usr.HomeDir, filepath.Separator, ".dockrampcache") 87 | cacheFile, err := os.OpenFile(cacheFilename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0600)) 88 | if err != nil { 89 | return fmt.Errorf("unable to open cache file: %s", err) 90 | } 91 | defer func() { 92 | if closeErr := cacheFile.Close(); err == nil { 93 | err = closeErr 94 | } 95 | }() 96 | 97 | if err := json.NewEncoder(cacheFile).Encode(b.cache); err != nil { 98 | return fmt.Errorf("unable to encode build cache: %s", err) 99 | } 100 | 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /build/commands/command.go: -------------------------------------------------------------------------------- 1 | // Package commands contains the set of Dockerfile commands. 2 | package commands 3 | 4 | // List of Dockerfile commands. 5 | const ( 6 | Add = "ADD" 7 | Cmd = "CMD" 8 | Copy = "COPY" 9 | Entrypoint = "ENTRYPOINT" 10 | Env = "ENV" 11 | Expose = "EXPOSE" 12 | Extract = "EXTRACT" 13 | From = "FROM" 14 | Label = "LABEL" 15 | Maintainer = "MAINTAINER" 16 | Onbuild = "ONBUILD" 17 | Run = "RUN" 18 | User = "USER" 19 | Volume = "VOLUME" 20 | Workdir = "WORKDIR" 21 | ) 22 | 23 | // Commands is a set of all Dockerfile commands. 24 | var Commands = map[string]struct{}{ 25 | Add: {}, 26 | Cmd: {}, 27 | Copy: {}, 28 | Entrypoint: {}, 29 | Env: {}, 30 | Expose: {}, 31 | Extract: {}, 32 | From: {}, 33 | Label: {}, 34 | Maintainer: {}, 35 | Onbuild: {}, 36 | Run: {}, 37 | User: {}, 38 | Volume: {}, 39 | Workdir: {}, 40 | } 41 | 42 | // FilesystemModifierCommands is a subset of commands that typically modify the 43 | // filesystem of a container and require a commit. 44 | var FilesystemModifierCommands = map[string]struct{}{ 45 | Add: {}, 46 | Copy: {}, 47 | Extract: {}, 48 | Run: {}, 49 | } 50 | 51 | // ReplaceEnvAllowed is a subset of commands for which environment variable 52 | // interpolation will happen. 53 | var ReplaceEnvAllowed = map[string]struct{}{ 54 | Add: {}, 55 | Copy: {}, 56 | Env: {}, 57 | Expose: {}, 58 | Extract: {}, 59 | Label: {}, 60 | User: {}, 61 | Volume: {}, 62 | Workdir: {}, 63 | } 64 | -------------------------------------------------------------------------------- /build/commit.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | 11 | log "github.com/Sirupsen/logrus" 12 | ) 13 | 14 | type containerCommitResponse struct { 15 | ID string `json:"Id"` 16 | } 17 | 18 | func (b *Builder) commit() error { 19 | log.Debugf("committing container: %s", b.containerID) 20 | 21 | if b.containerID == "" { 22 | return fmt.Errorf("no container to commit") 23 | } 24 | 25 | // Encode the uncommited commands as a JSON array to use as a comment. 26 | comment, err := json.Marshal(b.uncommittedCommands) 27 | if err != nil { 28 | return fmt.Errorf("unable to encode comment for commit: %s", err) 29 | } 30 | 31 | query := make(url.Values, 2) 32 | query.Set("container", b.containerID) 33 | query.Set("author", b.maintainer) 34 | query.Set("comment", string(comment)) 35 | 36 | data, err := json.Marshal(b.config.toDocker()) 37 | if err != nil { 38 | return fmt.Errorf("unable to encode config: %s", err) 39 | } 40 | 41 | path := fmt.Sprintf("/commit?%s", query.Encode()) 42 | req, err := http.NewRequest("POST", b.client.URL.String()+path, bytes.NewReader(data)) 43 | if err != nil { 44 | return fmt.Errorf("unable to prepare request: %s", err) 45 | } 46 | 47 | req.Header.Set("Content-Type", "application/json") 48 | 49 | resp, err := b.client.HTTPClient.Do(req) 50 | if err != nil { 51 | return fmt.Errorf("unable to make request: %s", err) 52 | } 53 | defer resp.Body.Close() 54 | 55 | if resp.StatusCode != http.StatusCreated { 56 | // Read the body if possible. 57 | buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength)) 58 | io.Copy(buf, resp.Body) // It's okay if this fails. 59 | 60 | return fmt.Errorf("request failed with status code %d: %s", resp.StatusCode, buf.String()) 61 | } 62 | 63 | var commitResponse containerCommitResponse 64 | if err := json.NewDecoder(resp.Body).Decode(&commitResponse); err != nil { 65 | return fmt.Errorf("unable to decode commit response: %s", err) 66 | } 67 | 68 | if err := b.client.RemoveContainer(b.containerID, true, true); err != nil { 69 | return fmt.Errorf("unable to remove container: %s", err) 70 | } 71 | 72 | if err := b.setCache(commitResponse.ID); err != nil { 73 | return fmt.Errorf("unable to cache commited image: %s", err) 74 | } 75 | 76 | b.imageID = commitResponse.ID 77 | 78 | fmt.Fprintf(b.out, " ---> %s\n", b.imageID) 79 | 80 | b.uncommitted = false 81 | b.uncommittedCommands = nil 82 | b.containerID = "" 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /build/extract.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | "os" 10 | "path/filepath" 11 | 12 | log "github.com/Sirupsen/logrus" 13 | "github.com/jlhawn/dockramp/build/commands" 14 | "github.com/jlhawn/tarsum" 15 | ) 16 | 17 | func (b *Builder) handleExtract(args []string, heredoc string) error { 18 | log.Debugf("handling %s with args: %#v", commands.Extract, args) 19 | 20 | if len(args) != 2 { 21 | return fmt.Errorf("%s requires exactly two arguments", commands.Extract) 22 | } 23 | 24 | if b.checkExtractCache(args[0]) { 25 | return nil 26 | } 27 | 28 | containerID, err := b.createContainer([]string{"/bin/sh", "-c"}, []string{"#(nop)"}, false) 29 | if err != nil { 30 | return fmt.Errorf("unable to create container: %s", err) 31 | } 32 | 33 | if err := b.extractToContainer(args[0], containerID, args[1]); err != nil { 34 | return fmt.Errorf("unable to copy to container: %s", err) 35 | } 36 | 37 | b.containerID = containerID 38 | 39 | return nil 40 | } 41 | 42 | func (b *Builder) checkExtractCache(srcPath string) bool { 43 | srcPath = fmt.Sprintf("%s%c%s", b.contextDirectory, filepath.Separator, srcPath) 44 | 45 | srcArchive, err := os.Open(srcPath) 46 | if err != nil { 47 | log.Debugf("unable to open source archive: %s", err) 48 | return false 49 | } 50 | defer srcArchive.Close() 51 | 52 | digester, err := tarsum.NewDigest(tarsum.Version1) 53 | if err != nil { 54 | log.Debugf("unable to get new tarsum digester: %s", err) 55 | return false 56 | } 57 | 58 | if _, err := io.Copy(digester, srcArchive); err != nil { 59 | log.Debugf("unable to digest source archive: %s", err) 60 | return false 61 | } 62 | 63 | copyDigest := fmt.Sprintf("%x", digester.Sum(nil)) 64 | b.uncommittedCommands = append(b.uncommittedCommands, fmt.Sprintf("EXTRACT digest: %s", copyDigest)) 65 | 66 | return b.probeCache() 67 | } 68 | 69 | func (b *Builder) extractToContainer(srcPath, dstContainer, dstDir string) (err error) { 70 | srcPath = fmt.Sprintf("%s%c%s", b.contextDirectory, filepath.Separator, srcPath) 71 | 72 | srcArchive, err := os.Open(srcPath) 73 | if err != nil { 74 | return fmt.Errorf("unable to open source archive: %s", err) 75 | } 76 | defer srcArchive.Close() 77 | 78 | query := make(url.Values, 1) 79 | query.Set("path", filepath.ToSlash(dstDir)) // Normalize the paths used in the API. 80 | 81 | urlPath := fmt.Sprintf("/containers/%s/extract-to-dir?%s", dstContainer, query.Encode()) 82 | req, err := http.NewRequest("PUT", b.client.URL.String()+urlPath, srcArchive) 83 | if err != nil { 84 | return fmt.Errorf("unable to prepare request: %s", err) 85 | } 86 | 87 | req.Header.Set("Content-Type", "application/x-tar") 88 | 89 | resp, err := b.client.HTTPClient.Do(req) 90 | if err != nil { 91 | return fmt.Errorf("unable to make request: %s", err) 92 | } 93 | defer resp.Body.Close() 94 | 95 | if resp.StatusCode != http.StatusOK { 96 | // Read the body if possible. 97 | buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength)) 98 | io.Copy(buf, resp.Body) // It's okay if this fails. 99 | 100 | return fmt.Errorf("request failed with status code %d: %s", resp.StatusCode, buf.String()) 101 | } 102 | 103 | return nil 104 | } 105 | -------------------------------------------------------------------------------- /build/from.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "fmt" 5 | 6 | log "github.com/Sirupsen/logrus" 7 | "github.com/jlhawn/dockramp/build/commands" 8 | "github.com/samalba/dockerclient" 9 | ) 10 | 11 | const ( 12 | fromScratch = "scratch" 13 | defaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 14 | ) 15 | 16 | func (b *Builder) handleFrom(args []string, heredoc string) error { 17 | log.Debugf("handling %s with args: %#v", commands.From, args) 18 | 19 | if len(args) != 1 { 20 | return fmt.Errorf("%s requires exactly one argument", commands.From) 21 | } 22 | 23 | imageName := args[0] 24 | 25 | if imageName == fromScratch { 26 | log.Debugf("building image from scratch") 27 | 28 | b.imageID = "" 29 | b.mergeConfig(nil) 30 | 31 | return nil 32 | } 33 | 34 | // See if it already exists. 35 | info, err := b.client.InspectImage(imageName) 36 | if err == nil { 37 | b.imageID = info.Id 38 | b.mergeConfig(info.Config) 39 | 40 | log.Debugf("got image ID: %s", b.imageID) 41 | 42 | return nil 43 | } 44 | 45 | if err != dockerclient.ErrNotFound { 46 | fmt.Errorf("unable to inspect image: %s", err) 47 | } 48 | 49 | // Need to pull the image. 50 | fmt.Fprintln(b.out, "pulling image ...") 51 | if err := b.client.PullImage(imageName, nil); err != nil { 52 | return fmt.Errorf("unable to pull image: %s", err) 53 | } 54 | 55 | // Inspect to get the ID. 56 | info, err = b.client.InspectImage(imageName) 57 | if err != nil { 58 | return fmt.Errorf("unable to inspect image: %s", err) 59 | } 60 | 61 | b.imageID = info.Id 62 | b.mergeConfig(info.Config) 63 | 64 | return nil 65 | } 66 | 67 | func (b *Builder) mergeConfig(config *dockerclient.ContainerConfig) { 68 | if config != nil { 69 | b.config.User = config.User 70 | b.config.ExposedPorts = config.ExposedPorts 71 | b.config.Env = config.Env 72 | b.config.Cmd = config.Cmd 73 | b.config.Volumes = config.Volumes 74 | b.config.WorkingDir = config.WorkingDir 75 | b.config.Entrypoint = config.Entrypoint 76 | b.config.Labels = config.Labels 77 | } 78 | 79 | if b.config.ExposedPorts == nil { 80 | b.config.ExposedPorts = map[string]struct{}{} 81 | } 82 | 83 | if b.config.Volumes == nil { 84 | b.config.Volumes = map[string]struct{}{} 85 | } 86 | 87 | if b.config.Labels == nil { 88 | b.config.Labels = map[string]string{} 89 | } 90 | 91 | if len(b.config.Env) == 0 { 92 | b.config.Env = []string{"PATH=" + defaultPathEnv} 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /build/handlers.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strings" 7 | 8 | log "github.com/Sirupsen/logrus" 9 | "github.com/jlhawn/dockramp/build/commands" 10 | ) 11 | 12 | /************************** 13 | * Unsupported Directives * 14 | **************************/ 15 | 16 | func (b *Builder) handleAdd(args []string, heredoc string) error { 17 | return fmt.Errorf("ADD not yet supported") 18 | } 19 | 20 | func (b *Builder) handleOnbuild(args []string, heredoc string) error { 21 | return fmt.Errorf("ONBUILD not yet supported") 22 | } 23 | 24 | /*********************** 25 | * Metadata Directives * 26 | ***********************/ 27 | 28 | func (b *Builder) handleCmd(args []string, heredoc string) error { 29 | log.Debugf("handling %s with args: %#v", commands.Cmd, args) 30 | 31 | b.config.Cmd = args 32 | 33 | return nil 34 | } 35 | 36 | func (b *Builder) handleEntrypoint(args []string, heredoc string) error { 37 | log.Debugf("handling %s with args: %#v", commands.Entrypoint, args) 38 | 39 | b.config.Entrypoint = args 40 | 41 | return nil 42 | } 43 | 44 | func (b *Builder) handleEnv(args []string, heredoc string) error { 45 | log.Debugf("handling %s with args: %#v", commands.Env, args) 46 | 47 | if len(args) != 2 { 48 | return fmt.Errorf("%s requires exactly two arguments", commands.Env) 49 | } 50 | 51 | b.config.Env = append(b.config.Env, fmt.Sprintf("%s=%s", args[0], args[1])) 52 | 53 | return nil 54 | } 55 | 56 | func (b *Builder) handleExpose(args []string, heredoc string) error { 57 | log.Debugf("handling %s with args: %#v", commands.Expose, args) 58 | 59 | if len(args) != 1 { 60 | return fmt.Errorf("%s requires exactly one argument", commands.Expose) 61 | } 62 | 63 | b.config.ExposedPorts[args[0]] = struct{}{} 64 | 65 | return nil 66 | } 67 | 68 | func (b *Builder) handleLabel(args []string, heredoc string) error { 69 | log.Debugf("handling %s with args: %#v", commands.Label, args) 70 | 71 | if len(args) != 2 { 72 | return fmt.Errorf("%s requires exactly two arguments", commands.Label) 73 | } 74 | 75 | b.config.Labels[args[0]] = args[1] 76 | 77 | return nil 78 | } 79 | 80 | func (b *Builder) handleMaintainer(args []string, heredoc string) error { 81 | log.Debugf("handling %s with args: %#v", commands.Maintainer, args) 82 | 83 | if len(args) < 1 { 84 | return fmt.Errorf("%s requires at least one argument", commands.Maintainer) 85 | } 86 | 87 | b.maintainer = strings.Join(args, " ") 88 | 89 | return nil 90 | } 91 | 92 | func (b *Builder) handleUser(args []string, heredoc string) error { 93 | log.Debugf("handling %s with args: %#v", commands.User, args) 94 | 95 | if len(args) != 1 { 96 | return fmt.Errorf("%s requires exactly one argument", commands.User) 97 | } 98 | 99 | b.config.User = args[0] 100 | 101 | return nil 102 | } 103 | 104 | func (b *Builder) handleVolume(args []string, heredoc string) error { 105 | log.Debugf("handling %s with args: %#v", commands.Volume, args) 106 | 107 | if len(args) == 0 { 108 | return fmt.Errorf("%s requires at least one argument", commands.Volume) 109 | } 110 | 111 | for _, arg := range args { 112 | vol := strings.TrimSpace(arg) 113 | if vol == "" { 114 | return fmt.Errorf("volume specified can not be an empty string") 115 | } 116 | 117 | b.config.Volumes[vol] = struct{}{} 118 | } 119 | 120 | return nil 121 | } 122 | 123 | func (b *Builder) handleWorkdir(args []string, heredoc string) error { 124 | log.Debugf("handling %s with args: %#v", commands.Workdir, args) 125 | 126 | if len(args) != 1 { 127 | return fmt.Errorf("%s requires exactly one argument", commands.Workdir) 128 | } 129 | 130 | workdir := args[0] 131 | if !filepath.IsAbs(workdir) { 132 | workdir = filepath.Join("/", b.config.WorkingDir, workdir) 133 | } 134 | 135 | b.config.WorkingDir = workdir 136 | 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /build/parser/cmd/parse_dockerfile/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/jlhawn/dockramp/build/parser" 9 | ) 10 | 11 | func main() { 12 | input := os.Stdin 13 | 14 | if len(os.Args) > 1 { 15 | var err error 16 | input, err = os.Open(os.Args[1]) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | defer input.Close() 21 | } 22 | 23 | commands, err := parser.Parse(input) 24 | if err != nil { 25 | log.Fatalf("unable to parse input: %s", err) 26 | } 27 | 28 | for _, cmd := range commands { 29 | fmt.Printf("%#v\n", cmd) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /build/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | // Command has arguments and an input literal from a heredoc. 10 | type Command struct { 11 | Args []string 12 | Heredoc string 13 | } 14 | 15 | // Parse parses the given input as a line-separated list of arguments. 16 | // On success, a slice of argument lists is returned. 17 | func Parse(input io.Reader) (commands []*Command, err error) { 18 | scanner := bufio.NewScanner(input) 19 | 20 | var currentToken token 21 | scanner.Split(tokenize(¤tToken)) 22 | 23 | var tokens []token 24 | for scanner.Scan() { 25 | tokens = append(tokens, currentToken) 26 | 27 | if numTokens := len(tokens); numTokens > 1 { 28 | prevToken := tokens[numTokens-2] 29 | if mergedToken := prevToken.Merge(currentToken); mergedToken != nil { 30 | tokens[numTokens-2] = mergedToken 31 | tokens = tokens[:numTokens-1] 32 | } 33 | } 34 | } 35 | 36 | if err := scanner.Err(); err != nil { 37 | return nil, err 38 | } 39 | 40 | beginning := true 41 | var currentCommand *Command 42 | for _, token := range tokens { 43 | if token.Type() == tokenTypeWhitespace { 44 | continue // Ignore whitespace tokens. 45 | } 46 | 47 | if token.Type() == tokenTypeNewline { 48 | if !beginning { // handle leading newlines. 49 | // Newline signals the end of a command. 50 | commands = append(commands, currentCommand) 51 | currentCommand = nil 52 | } 53 | continue 54 | } 55 | 56 | if token.Type() == tokenTypeHeredoc { 57 | if currentCommand == nil { 58 | return nil, errors.New("unexpected heredoc") 59 | } 60 | 61 | currentCommand.Heredoc = token.Value() 62 | 63 | // Heredoc also signals the end of a command. 64 | commands = append(commands, currentCommand) 65 | currentCommand = nil 66 | 67 | continue 68 | } 69 | 70 | if token.Type() != tokenTypeArg { 71 | return nil, errors.New("unexpected token") 72 | } 73 | 74 | beginning = false 75 | // Append arg to current command. 76 | if currentCommand == nil { 77 | currentCommand = &Command{} 78 | } 79 | currentCommand.Args = append(currentCommand.Args, token.Value()) 80 | } 81 | 82 | if currentCommand != nil { 83 | // Handles case with no trailing newline. 84 | commands = append(commands, currentCommand) 85 | } 86 | 87 | return commands, nil 88 | } 89 | -------------------------------------------------------------------------------- /build/parser/token.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | type tokenType int 4 | 5 | const ( 6 | tokenTypeArg tokenType = iota 7 | tokenTypeWhitespace 8 | tokenTypeHeredoc 9 | tokenTypeNewline 10 | ) 11 | 12 | type token interface { 13 | Type() tokenType 14 | Value() string 15 | // Merge should attempt to merge this token with the given next token. The 16 | // tokens should already be evaluated. If the tokens cannot be merged then 17 | // Merge returns nil. 18 | Merge(next token) token 19 | } 20 | 21 | type argToken string 22 | 23 | func (t argToken) Type() tokenType { 24 | return tokenTypeArg 25 | } 26 | 27 | func (t argToken) Value() string { 28 | return string(t) 29 | } 30 | 31 | func (t argToken) Merge(next token) token { 32 | if next.Type() != tokenTypeArg { 33 | return nil 34 | } 35 | 36 | return argToken(string(t) + next.Value()) 37 | } 38 | 39 | type whitespaceToken struct{} 40 | 41 | func (t whitespaceToken) Type() tokenType { 42 | return tokenTypeWhitespace 43 | } 44 | 45 | func (t whitespaceToken) Value() string { 46 | return "" 47 | } 48 | 49 | func (t whitespaceToken) Merge(next token) token { 50 | switch next.Type() { 51 | case tokenTypeWhitespace: 52 | return whitespaceToken{} 53 | case tokenTypeNewline: 54 | return newlineToken{} 55 | default: 56 | return nil 57 | } 58 | } 59 | 60 | type newlineToken struct{} 61 | 62 | func (t newlineToken) Type() tokenType { 63 | return tokenTypeNewline 64 | } 65 | 66 | func (t newlineToken) Value() string { 67 | return "" 68 | } 69 | 70 | func (t newlineToken) Merge(next token) token { 71 | switch next.Type() { 72 | case tokenTypeWhitespace, tokenTypeNewline: 73 | return newlineToken{} 74 | default: 75 | return nil 76 | } 77 | } 78 | 79 | type heredocToken string 80 | 81 | func (t heredocToken) Type() tokenType { 82 | return tokenTypeHeredoc 83 | } 84 | 85 | func (t heredocToken) Value() string { 86 | return string(t) 87 | } 88 | 89 | func (t heredocToken) Merge(next token) token { 90 | switch next.Type() { 91 | case tokenTypeWhitespace, tokenTypeNewline: 92 | return t 93 | default: 94 | return nil 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /build/run.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/url" 7 | "strings" 8 | 9 | log "github.com/Sirupsen/logrus" 10 | "github.com/docker/docker/pkg/stdcopy" 11 | "github.com/jlhawn/dockramp/build/commands" 12 | ) 13 | 14 | func (b *Builder) handleRun(args []string, heredoc string) error { 15 | log.Debugf("handling %s with args: %#v", commands.Run, args) 16 | 17 | if len(args) < 1 { 18 | return fmt.Errorf("%s requires at least one argument", commands.Run) 19 | } 20 | 21 | if heredoc != "" { 22 | fmt.Fprintf(b.out, "Input:\n%s\n", heredoc) 23 | b.uncommittedCommands = append(b.uncommittedCommands, fmt.Sprintf("RUN input: %q", heredoc)) 24 | } 25 | 26 | if b.probeCache() { 27 | return nil 28 | } 29 | 30 | containerID, err := b.createContainer(args[:1], args[1:], true) 31 | if err != nil { 32 | return fmt.Errorf("unable to create container: %s", err) 33 | } 34 | 35 | errC, err := b.attachContainer(containerID, strings.NewReader(heredoc)) 36 | if err != nil { 37 | return fmt.Errorf("unable to attach to container: %s", err) 38 | } 39 | 40 | if err := b.client.StartContainer(containerID, nil); err != nil { 41 | return fmt.Errorf("unable to start container: %s", err) 42 | } 43 | 44 | // Wait for the container hijack to end. 45 | if err := <-errC; err != nil { 46 | return fmt.Errorf("unable to end hijack stream: %s", err) 47 | } 48 | 49 | if err := b.client.StopContainer(containerID, 1); err != nil { 50 | return fmt.Errorf("unable to stop/kill container: %s", err) 51 | } 52 | 53 | info, err := b.client.InspectContainer(containerID) 54 | if err != nil { 55 | return fmt.Errorf("unable to inspect container: %s", err) 56 | } 57 | 58 | if info.State.ExitCode != 0 { 59 | return fmt.Errorf("non-zero exit code: %d", info.State.ExitCode) 60 | } 61 | 62 | b.containerID = containerID 63 | 64 | return nil 65 | } 66 | 67 | func (b *Builder) createContainer(entryPoint, cmd []string, openStdin bool) (containerID string, err error) { 68 | config := b.config.toDocker() 69 | config.Entrypoint = entryPoint 70 | config.Cmd = cmd 71 | config.Image = b.imageID 72 | config.OpenStdin = openStdin 73 | config.StdinOnce = openStdin 74 | 75 | return b.client.CreateContainer(config, "", nil) 76 | } 77 | 78 | func (b *Builder) attachContainer(container string, input io.Reader) (chan error, error) { 79 | query := make(url.Values, 4) 80 | query.Set("stream", "true") 81 | query.Set("stdin", "true") 82 | query.Set("stdout", "true") 83 | query.Set("stderr", "true") 84 | 85 | urlPath := fmt.Sprintf("/containers/%s/attach?%s", container, query.Encode()) 86 | 87 | hijackStarted := make(chan int, 1) 88 | hijackErr := make(chan error, 1) 89 | 90 | // The output from /attach will be a multiplexed stream of stdout and 91 | // stderr. We need to use a pipe to copy this output into a stdcopy 92 | // de-multiplexer and into the build output. 93 | pipeReader, pipeWriter := io.Pipe() 94 | go func() { 95 | defer pipeReader.Close() 96 | stdcopy.StdCopy(b.out, b.out, pipeReader) 97 | }() 98 | 99 | go func() { 100 | hijackErr <- b.hijack("POST", urlPath, input, pipeWriter, hijackStarted) 101 | }() 102 | 103 | // Wait for the hijack to succeeed or fail. 104 | select { 105 | case <-hijackStarted: 106 | return hijackErr, nil 107 | case err := <-hijackErr: 108 | return nil, fmt.Errorf("unable to hijack attach tcp stream: %s", err) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /build/shell_parser_test.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestShellParser(t *testing.T) { 11 | file, err := os.Open("words") 12 | if err != nil { 13 | t.Fatalf("Can't open 'words': %s", err) 14 | } 15 | defer file.Close() 16 | 17 | scanner := bufio.NewScanner(file) 18 | envs := []string{"PWD=/home", "SHELL=bash"} 19 | for scanner.Scan() { 20 | line := scanner.Text() 21 | 22 | // Trim comments and blank lines 23 | i := strings.Index(line, "#") 24 | if i >= 0 { 25 | line = line[:i] 26 | } 27 | line = strings.TrimSpace(line) 28 | 29 | if line == "" { 30 | continue 31 | } 32 | 33 | words := strings.Split(line, "|") 34 | if len(words) != 2 { 35 | t.Fatalf("Error in 'words' - should be 2 words:%q", words) 36 | } 37 | 38 | words[0] = strings.TrimSpace(words[0]) 39 | words[1] = strings.TrimSpace(words[1]) 40 | 41 | newWord, err := ProcessWord(words[0], envs) 42 | 43 | if err != nil { 44 | newWord = "error" 45 | } 46 | 47 | if newWord != words[1] { 48 | t.Fatalf("Error. Src: %s Calc: %s Expected: %s", words[0], newWord, words[1]) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /build/tag.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | func (b *Builder) setTag(imgID, repo, tag string) error { 12 | query := make(url.Values, 3) 13 | query.Set("repo", repo) 14 | query.Set("tag", tag) 15 | query.Set("force", "1") 16 | 17 | urlPath := fmt.Sprintf("/images/%s/tag?%s", imgID, query.Encode()) 18 | req, err := http.NewRequest("POST", b.client.URL.String()+urlPath, nil) 19 | if err != nil { 20 | return fmt.Errorf("unable to prepare request: %s", err) 21 | } 22 | 23 | resp, err := b.client.HTTPClient.Do(req) 24 | if err != nil { 25 | return fmt.Errorf("unable to make request: %s", err) 26 | } 27 | defer resp.Body.Close() 28 | 29 | if resp.StatusCode != http.StatusCreated { 30 | // Read the body if possible. 31 | buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength)) 32 | io.Copy(buf, resp.Body) // It's okay if this fails. 33 | 34 | return fmt.Errorf("request failed with status code %d: %s", resp.StatusCode, buf.String()) 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /build/words: -------------------------------------------------------------------------------- 1 | hello | hello 2 | he'll'o | hello 3 | he'llo | hello 4 | he\'llo | he'llo 5 | he\\'llo | he\llo 6 | abc\tdef | abctdef 7 | "abc\tdef" | abc\tdef 8 | 'abc\tdef' | abc\tdef 9 | hello\ | hello 10 | hello\\ | hello\ 11 | "hello | hello 12 | "hello\" | hello" 13 | "hel'lo" | hel'lo 14 | 'hello | hello 15 | 'hello\' | hello\ 16 | "''" | '' 17 | $. | $. 18 | $1 | 19 | he$1x | hex 20 | he$.x | he$.x 21 | he$pwd. | he. 22 | he$PWD | he/home 23 | he\$PWD | he$PWD 24 | he\\$PWD | he\/home 25 | he\${} | he${} 26 | he\${}xx | he${}xx 27 | he${} | he 28 | he${}xx | hexx 29 | he${hi} | he 30 | he${hi}xx | hexx 31 | he${PWD} | he/home 32 | he${.} | error 33 | he${XXX:-000}xx | he000xx 34 | he${PWD:-000}xx | he/homexx 35 | he${XXX:-$PWD}xx | he/homexx 36 | he${XXX:-${PWD:-yyy}}xx | he/homexx 37 | he${XXX:-${YYY:-yyy}}xx | heyyyxx 38 | he${XXX:YYY} | error 39 | he${XXX:+${PWD}}xx | hexx 40 | he${PWD:+${XXX}}xx | hexx 41 | he${PWD:+${SHELL}}xx | hebashxx 42 | he${XXX:+000}xx | hexx 43 | he${PWD:+000}xx | he000xx 44 | 'he${XX}' | he${XX} 45 | "he${PWD}" | he/home 46 | "he'$PWD'" | he'/home' 47 | "$PWD" | /home 48 | '$PWD' | $PWD 49 | '\$PWD' | \$PWD 50 | '"hello"' | "hello" 51 | he\$PWD | he$PWD 52 | "he\$PWD" | he$PWD 53 | 'he\$PWD' | he\$PWD 54 | he${PWD | error 55 | he${PWD:=000}xx | error 56 | he${PWD:+${PWD}:}xx | he/home:xx 57 | he${XXX:-\$PWD:}xx | he$PWD:xx 58 | he${XXX:-\${PWD}z}xx | he${PWDz}xx 59 | -------------------------------------------------------------------------------- /dockramp.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "GoSublime": { 4 | "env": { 5 | "GOPATH": "$HOME/gopaths/dockramp" 6 | } 7 | } 8 | }, 9 | "folders": 10 | [ 11 | { 12 | "path": "." 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /make_binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FAILURES="" 4 | TARGET="github.com/jlhawn/dockramp/cmd/dockramp" 5 | 6 | mkdir "$1" 7 | 8 | for PLATFORM in $PLATFORMS; do 9 | OUTPUTDIR="$1/$PLATFORM" 10 | mkdir -p "$OUTPUTDIR" 11 | 12 | export GOPATH="$PROJ_DIR/Godeps/_workspace:$GOPATH" 13 | export GOOS="${PLATFORM%/*}" 14 | export GOARCH="${PLATFORM#*/}" 15 | 16 | CMD="go build -o $OUTPUTDIR/dockramp $TARGET" 17 | 18 | echo "$CMD" && $CMD || FAILURES="$FAILURES $PLATFORM" 19 | done 20 | 21 | if [ -n "$FAILURES" ]; then 22 | echo "*** build FAILED on $FAILURES ***" 23 | exit 1 24 | fi 25 | --------------------------------------------------------------------------------