├── .gitignore ├── .travis.yml ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── github.com │ ├── Sirupsen │ │ └── logrus │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── entry.go │ │ │ ├── entry_test.go │ │ │ ├── examples │ │ │ ├── basic │ │ │ │ └── basic.go │ │ │ └── hook │ │ │ │ └── hook.go │ │ │ ├── exported.go │ │ │ ├── formatter.go │ │ │ ├── formatter_bench_test.go │ │ │ ├── hook_test.go │ │ │ ├── hooks.go │ │ │ ├── hooks │ │ │ ├── airbrake │ │ │ │ └── airbrake.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 │ │ │ ├── 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 │ ├── alecthomas │ │ ├── template │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── example_test.go │ │ │ ├── examplefiles_test.go │ │ │ ├── examplefunc_test.go │ │ │ ├── exec.go │ │ │ ├── exec_test.go │ │ │ ├── funcs.go │ │ │ ├── helper.go │ │ │ ├── multi_test.go │ │ │ ├── parse │ │ │ │ ├── lex.go │ │ │ │ ├── lex_test.go │ │ │ │ ├── node.go │ │ │ │ ├── parse.go │ │ │ │ └── parse_test.go │ │ │ ├── template.go │ │ │ └── testdata │ │ │ │ ├── file1.tmpl │ │ │ │ ├── file2.tmpl │ │ │ │ ├── tmpl1.tmpl │ │ │ │ └── tmpl2.tmpl │ │ └── units │ │ │ ├── COPYING │ │ │ ├── README.md │ │ │ ├── bytes.go │ │ │ ├── bytes_test.go │ │ │ ├── doc.go │ │ │ ├── si.go │ │ │ └── util.go │ └── mitchellh │ │ └── go-homedir │ │ ├── LICENSE │ │ ├── README.md │ │ ├── homedir.go │ │ └── homedir_test.go │ └── gopkg.in │ └── alecthomas │ └── kingpin.v2 │ ├── .travis.yml │ ├── COPYING │ ├── README.md │ ├── app.go │ ├── app_test.go │ ├── args.go │ ├── args_test.go │ ├── cmd.go │ ├── cmd_test.go │ ├── doc.go │ ├── examples │ ├── chat1 │ │ └── main.go │ ├── chat2 │ │ └── main.go │ ├── curl │ │ └── main.go │ ├── modular │ │ └── main.go │ └── ping │ │ └── main.go │ ├── examples_test.go │ ├── flags.go │ ├── flags_test.go │ ├── genrepeated │ └── main.go │ ├── global.go │ ├── guesswidth.go │ ├── guesswidth_unix.go │ ├── model.go │ ├── parser.go │ ├── parser_test.go │ ├── parsers.go │ ├── parsers_test.go │ ├── repeated.go │ ├── repeated.json │ ├── templates.go │ ├── usage.go │ ├── usage_test.go │ └── values.go ├── LICENSE ├── README.md ├── cli ├── cli.go └── cli_test.go ├── lib ├── downloader.go ├── lib.go └── lib_test.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | dkenv 2 | .dkenv.go.swp 3 | coverage.out 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/newrelic/dkenv", 3 | "GoVersion": "go1.5", 4 | "Deps": [ 5 | { 6 | "ImportPath": "github.com/Sirupsen/logrus", 7 | "Comment": "v0.6.4-6-g539d4dc", 8 | "Rev": "539d4dc034c079a7188b5d4ca9650632d73c66e8" 9 | }, 10 | { 11 | "ImportPath": "github.com/alecthomas/template", 12 | "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0" 13 | }, 14 | { 15 | "ImportPath": "github.com/alecthomas/units", 16 | "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915" 17 | }, 18 | { 19 | "ImportPath": "github.com/mitchellh/go-homedir", 20 | "Rev": "1f6da4a72e57d4e7edd4a7295a585e0a3999a2d4" 21 | }, 22 | { 23 | "ImportPath": "gopkg.in/alecthomas/kingpin.v2", 24 | "Comment": "v2.0.11", 25 | "Rev": "3eb8ffbc54a2f5e806181081e23098b67fe06d06" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /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/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.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "time" 9 | ) 10 | 11 | // An entry is the final or intermediate Logrus logging entry. It contains all 12 | // the fields passed with WithField{,s}. It's finally logged when Debug, Info, 13 | // Warn, Error, Fatal or Panic is called on it. These objects can be reused and 14 | // passed around as much as you wish to avoid field duplication. 15 | type Entry struct { 16 | Logger *Logger 17 | 18 | // Contains all the fields set by the user. 19 | Data Fields 20 | 21 | // Time at which the log entry was created 22 | Time time.Time 23 | 24 | // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic 25 | Level Level 26 | 27 | // Message passed to Debug, Info, Warn, Error, Fatal or Panic 28 | Message string 29 | } 30 | 31 | func NewEntry(logger *Logger) *Entry { 32 | return &Entry{ 33 | Logger: logger, 34 | // Default is three fields, give a little extra room 35 | Data: make(Fields, 5), 36 | } 37 | } 38 | 39 | // Returns a reader for the entry, which is a proxy to the formatter. 40 | func (entry *Entry) Reader() (*bytes.Buffer, error) { 41 | serialized, err := entry.Logger.Formatter.Format(entry) 42 | return bytes.NewBuffer(serialized), err 43 | } 44 | 45 | // Returns the string representation from the reader and ultimately the 46 | // formatter. 47 | func (entry *Entry) String() (string, error) { 48 | reader, err := entry.Reader() 49 | if err != nil { 50 | return "", err 51 | } 52 | 53 | return reader.String(), err 54 | } 55 | 56 | // Add a single field to the Entry. 57 | func (entry *Entry) WithField(key string, value interface{}) *Entry { 58 | return entry.WithFields(Fields{key: value}) 59 | } 60 | 61 | // Add a map of fields to the Entry. 62 | func (entry *Entry) WithFields(fields Fields) *Entry { 63 | data := Fields{} 64 | for k, v := range entry.Data { 65 | data[k] = v 66 | } 67 | for k, v := range fields { 68 | data[k] = v 69 | } 70 | return &Entry{Logger: entry.Logger, Data: data} 71 | } 72 | 73 | func (entry *Entry) log(level Level, msg string) { 74 | entry.Time = time.Now() 75 | entry.Level = level 76 | entry.Message = msg 77 | 78 | if err := entry.Logger.Hooks.Fire(level, entry); err != nil { 79 | entry.Logger.mu.Lock() 80 | fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) 81 | entry.Logger.mu.Unlock() 82 | } 83 | 84 | reader, err := entry.Reader() 85 | if err != nil { 86 | entry.Logger.mu.Lock() 87 | fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) 88 | entry.Logger.mu.Unlock() 89 | } 90 | 91 | entry.Logger.mu.Lock() 92 | defer entry.Logger.mu.Unlock() 93 | 94 | _, err = io.Copy(entry.Logger.Out, reader) 95 | if err != nil { 96 | fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) 97 | } 98 | 99 | // To avoid Entry#log() returning a value that only would make sense for 100 | // panic() to use in Entry#Panic(), we avoid the allocation by checking 101 | // directly here. 102 | if level <= PanicLevel { 103 | panic(entry) 104 | } 105 | } 106 | 107 | func (entry *Entry) Debug(args ...interface{}) { 108 | if entry.Logger.Level >= DebugLevel { 109 | entry.log(DebugLevel, fmt.Sprint(args...)) 110 | } 111 | } 112 | 113 | func (entry *Entry) Print(args ...interface{}) { 114 | entry.Info(args...) 115 | } 116 | 117 | func (entry *Entry) Info(args ...interface{}) { 118 | if entry.Logger.Level >= InfoLevel { 119 | entry.log(InfoLevel, fmt.Sprint(args...)) 120 | } 121 | } 122 | 123 | func (entry *Entry) Warn(args ...interface{}) { 124 | if entry.Logger.Level >= WarnLevel { 125 | entry.log(WarnLevel, fmt.Sprint(args...)) 126 | } 127 | } 128 | 129 | func (entry *Entry) Warning(args ...interface{}) { 130 | entry.Warn(args...) 131 | } 132 | 133 | func (entry *Entry) Error(args ...interface{}) { 134 | if entry.Logger.Level >= ErrorLevel { 135 | entry.log(ErrorLevel, fmt.Sprint(args...)) 136 | } 137 | } 138 | 139 | func (entry *Entry) Fatal(args ...interface{}) { 140 | if entry.Logger.Level >= FatalLevel { 141 | entry.log(FatalLevel, fmt.Sprint(args...)) 142 | } 143 | os.Exit(1) 144 | } 145 | 146 | func (entry *Entry) Panic(args ...interface{}) { 147 | if entry.Logger.Level >= PanicLevel { 148 | entry.log(PanicLevel, fmt.Sprint(args...)) 149 | } 150 | panic(fmt.Sprint(args...)) 151 | } 152 | 153 | // Entry Printf family functions 154 | 155 | func (entry *Entry) Debugf(format string, args ...interface{}) { 156 | if entry.Logger.Level >= DebugLevel { 157 | entry.Debug(fmt.Sprintf(format, args...)) 158 | } 159 | } 160 | 161 | func (entry *Entry) Infof(format string, args ...interface{}) { 162 | if entry.Logger.Level >= InfoLevel { 163 | entry.Info(fmt.Sprintf(format, args...)) 164 | } 165 | } 166 | 167 | func (entry *Entry) Printf(format string, args ...interface{}) { 168 | entry.Infof(format, args...) 169 | } 170 | 171 | func (entry *Entry) Warnf(format string, args ...interface{}) { 172 | if entry.Logger.Level >= WarnLevel { 173 | entry.Warn(fmt.Sprintf(format, args...)) 174 | } 175 | } 176 | 177 | func (entry *Entry) Warningf(format string, args ...interface{}) { 178 | entry.Warnf(format, args...) 179 | } 180 | 181 | func (entry *Entry) Errorf(format string, args ...interface{}) { 182 | if entry.Logger.Level >= ErrorLevel { 183 | entry.Error(fmt.Sprintf(format, args...)) 184 | } 185 | } 186 | 187 | func (entry *Entry) Fatalf(format string, args ...interface{}) { 188 | if entry.Logger.Level >= FatalLevel { 189 | entry.Fatal(fmt.Sprintf(format, args...)) 190 | } 191 | } 192 | 193 | func (entry *Entry) Panicf(format string, args ...interface{}) { 194 | if entry.Logger.Level >= PanicLevel { 195 | entry.Panic(fmt.Sprintf(format, args...)) 196 | } 197 | } 198 | 199 | // Entry Println family functions 200 | 201 | func (entry *Entry) Debugln(args ...interface{}) { 202 | if entry.Logger.Level >= DebugLevel { 203 | entry.Debug(entry.sprintlnn(args...)) 204 | } 205 | } 206 | 207 | func (entry *Entry) Infoln(args ...interface{}) { 208 | if entry.Logger.Level >= InfoLevel { 209 | entry.Info(entry.sprintlnn(args...)) 210 | } 211 | } 212 | 213 | func (entry *Entry) Println(args ...interface{}) { 214 | entry.Infoln(args...) 215 | } 216 | 217 | func (entry *Entry) Warnln(args ...interface{}) { 218 | if entry.Logger.Level >= WarnLevel { 219 | entry.Warn(entry.sprintlnn(args...)) 220 | } 221 | } 222 | 223 | func (entry *Entry) Warningln(args ...interface{}) { 224 | entry.Warnln(args...) 225 | } 226 | 227 | func (entry *Entry) Errorln(args ...interface{}) { 228 | if entry.Logger.Level >= ErrorLevel { 229 | entry.Error(entry.sprintlnn(args...)) 230 | } 231 | } 232 | 233 | func (entry *Entry) Fatalln(args ...interface{}) { 234 | if entry.Logger.Level >= FatalLevel { 235 | entry.Fatal(entry.sprintlnn(args...)) 236 | } 237 | } 238 | 239 | func (entry *Entry) Panicln(args ...interface{}) { 240 | if entry.Logger.Level >= PanicLevel { 241 | entry.Panic(entry.sprintlnn(args...)) 242 | } 243 | } 244 | 245 | // Sprintlnn => Sprint no newline. This is to get the behavior of how 246 | // fmt.Sprintln where spaces are always added between operands, regardless of 247 | // their type. Instead of vendoring the Sprintln implementation to spare a 248 | // string allocation, we do the simplest thing. 249 | func (entry *Entry) sprintlnn(args ...interface{}) string { 250 | msg := fmt.Sprintln(args...) 251 | return msg[:len(msg)-1] 252 | } 253 | -------------------------------------------------------------------------------- /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 | } 13 | 14 | func main() { 15 | defer func() { 16 | err := recover() 17 | if err != nil { 18 | log.WithFields(logrus.Fields{ 19 | "omg": true, 20 | "err": err, 21 | "number": 100, 22 | }).Fatal("The ice breaks!") 23 | } 24 | }() 25 | 26 | log.WithFields(logrus.Fields{ 27 | "animal": "walrus", 28 | "size": 10, 29 | }).Info("A group of walrus emerges from the ocean") 30 | 31 | log.WithFields(logrus.Fields{ 32 | "omg": true, 33 | "number": 122, 34 | }).Warn("The group's number increased tremendously!") 35 | 36 | log.WithFields(logrus.Fields{ 37 | "animal": "orca", 38 | "size": 9009, 39 | }).Panic("It's over 9000!") 40 | } 41 | -------------------------------------------------------------------------------- /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 | "github.com/tobi/airbrake-go" 7 | ) 8 | 9 | var log = logrus.New() 10 | 11 | func init() { 12 | log.Formatter = new(logrus.TextFormatter) // default 13 | log.Hooks.Add(new(logrus_airbrake.AirbrakeHook)) 14 | } 15 | 16 | func main() { 17 | airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml" 18 | airbrake.ApiKey = "whatever" 19 | airbrake.Environment = "production" 20 | 21 | log.WithFields(logrus.Fields{ 22 | "animal": "walrus", 23 | "size": 10, 24 | }).Info("A group of walrus emerges from the ocean") 25 | 26 | log.WithFields(logrus.Fields{ 27 | "omg": true, 28 | "number": 122, 29 | }).Warn("The group's number increased tremendously!") 30 | 31 | log.WithFields(logrus.Fields{ 32 | "omg": true, 33 | "number": 100, 34 | }).Fatal("The ice breaks!") 35 | } 36 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | var ( 8 | // std is the name of the standard logger in stdlib `log` 9 | std = New() 10 | ) 11 | 12 | // SetOutput sets the standard logger output. 13 | func SetOutput(out io.Writer) { 14 | std.mu.Lock() 15 | defer std.mu.Unlock() 16 | std.Out = out 17 | } 18 | 19 | // SetFormatter sets the standard logger formatter. 20 | func SetFormatter(formatter Formatter) { 21 | std.mu.Lock() 22 | defer std.mu.Unlock() 23 | std.Formatter = formatter 24 | } 25 | 26 | // SetLevel sets the standard logger level. 27 | func SetLevel(level Level) { 28 | std.mu.Lock() 29 | defer std.mu.Unlock() 30 | std.Level = level 31 | } 32 | 33 | // GetLevel returns the standard logger level. 34 | func GetLevel() Level { 35 | return std.Level 36 | } 37 | 38 | // AddHook adds a hook to the standard logger hooks. 39 | func AddHook(hook Hook) { 40 | std.mu.Lock() 41 | defer std.mu.Unlock() 42 | std.Hooks.Add(hook) 43 | } 44 | 45 | // WithField creates an entry from the standard logger and adds a field to 46 | // it. If you want multiple fields, use `WithFields`. 47 | // 48 | // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal 49 | // or Panic on the Entry it returns. 50 | func WithField(key string, value interface{}) *Entry { 51 | return std.WithField(key, value) 52 | } 53 | 54 | // WithFields creates an entry from the standard logger and adds multiple 55 | // fields to it. This is simply a helper for `WithField`, invoking it 56 | // once for each field. 57 | // 58 | // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal 59 | // or Panic on the Entry it returns. 60 | func WithFields(fields Fields) *Entry { 61 | return std.WithFields(fields) 62 | } 63 | 64 | // Debug logs a message at level Debug on the standard logger. 65 | func Debug(args ...interface{}) { 66 | std.Debug(args...) 67 | } 68 | 69 | // Print logs a message at level Info on the standard logger. 70 | func Print(args ...interface{}) { 71 | std.Print(args...) 72 | } 73 | 74 | // Info logs a message at level Info on the standard logger. 75 | func Info(args ...interface{}) { 76 | std.Info(args...) 77 | } 78 | 79 | // Warn logs a message at level Warn on the standard logger. 80 | func Warn(args ...interface{}) { 81 | std.Warn(args...) 82 | } 83 | 84 | // Warning logs a message at level Warn on the standard logger. 85 | func Warning(args ...interface{}) { 86 | std.Warning(args...) 87 | } 88 | 89 | // Error logs a message at level Error on the standard logger. 90 | func Error(args ...interface{}) { 91 | std.Error(args...) 92 | } 93 | 94 | // Panic logs a message at level Panic on the standard logger. 95 | func Panic(args ...interface{}) { 96 | std.Panic(args...) 97 | } 98 | 99 | // Fatal logs a message at level Fatal on the standard logger. 100 | func Fatal(args ...interface{}) { 101 | std.Fatal(args...) 102 | } 103 | 104 | // Debugf logs a message at level Debug on the standard logger. 105 | func Debugf(format string, args ...interface{}) { 106 | std.Debugf(format, args...) 107 | } 108 | 109 | // Printf logs a message at level Info on the standard logger. 110 | func Printf(format string, args ...interface{}) { 111 | std.Printf(format, args...) 112 | } 113 | 114 | // Infof logs a message at level Info on the standard logger. 115 | func Infof(format string, args ...interface{}) { 116 | std.Infof(format, args...) 117 | } 118 | 119 | // Warnf logs a message at level Warn on the standard logger. 120 | func Warnf(format string, args ...interface{}) { 121 | std.Warnf(format, args...) 122 | } 123 | 124 | // Warningf logs a message at level Warn on the standard logger. 125 | func Warningf(format string, args ...interface{}) { 126 | std.Warningf(format, args...) 127 | } 128 | 129 | // Errorf logs a message at level Error on the standard logger. 130 | func Errorf(format string, args ...interface{}) { 131 | std.Errorf(format, args...) 132 | } 133 | 134 | // Panicf logs a message at level Panic on the standard logger. 135 | func Panicf(format string, args ...interface{}) { 136 | std.Panicf(format, args...) 137 | } 138 | 139 | // Fatalf logs a message at level Fatal on the standard logger. 140 | func Fatalf(format string, args ...interface{}) { 141 | std.Fatalf(format, args...) 142 | } 143 | 144 | // Debugln logs a message at level Debug on the standard logger. 145 | func Debugln(args ...interface{}) { 146 | std.Debugln(args...) 147 | } 148 | 149 | // Println logs a message at level Info on the standard logger. 150 | func Println(args ...interface{}) { 151 | std.Println(args...) 152 | } 153 | 154 | // Infoln logs a message at level Info on the standard logger. 155 | func Infoln(args ...interface{}) { 156 | std.Infoln(args...) 157 | } 158 | 159 | // Warnln logs a message at level Warn on the standard logger. 160 | func Warnln(args ...interface{}) { 161 | std.Warnln(args...) 162 | } 163 | 164 | // Warningln logs a message at level Warn on the standard logger. 165 | func Warningln(args ...interface{}) { 166 | std.Warningln(args...) 167 | } 168 | 169 | // Errorln logs a message at level Error on the standard logger. 170 | func Errorln(args ...interface{}) { 171 | std.Errorln(args...) 172 | } 173 | 174 | // Panicln logs a message at level Panic on the standard logger. 175 | func Panicln(args ...interface{}) { 176 | std.Panicln(args...) 177 | } 178 | 179 | // Fatalln logs a message at level Fatal on the standard logger. 180 | func Fatalln(args ...interface{}) { 181 | std.Fatalln(args...) 182 | } 183 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | // The Formatter interface is used to implement a custom Formatter. It takes an 4 | // `Entry`. It exposes all the fields, including the default ones: 5 | // 6 | // * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. 7 | // * `entry.Data["time"]`. The timestamp. 8 | // * `entry.Data["level"]. The level the entry was logged at. 9 | // 10 | // Any additional fields added with `WithField` or `WithFields` are also in 11 | // `entry.Data`. Format is expected to return an array of bytes which are then 12 | // logged to `logger.Out`. 13 | type Formatter interface { 14 | Format(*Entry) ([]byte, error) 15 | } 16 | 17 | // This is to not silently overwrite `time`, `msg` and `level` fields when 18 | // dumping it. If this code wasn't there doing: 19 | // 20 | // logrus.WithField("level", 1).Info("hello") 21 | // 22 | // Would just silently drop the user provided level. Instead with this code 23 | // it'll logged as: 24 | // 25 | // {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} 26 | // 27 | // It's not exported because it's still using Data in an opinionated way. It's to 28 | // avoid code duplication between the two default formatters. 29 | func prefixFieldClashes(data Fields) { 30 | _, ok := data["time"] 31 | if ok { 32 | data["fields.time"] = data["time"] 33 | } 34 | 35 | _, ok = data["msg"] 36 | if ok { 37 | data["fields.msg"] = data["msg"] 38 | } 39 | 40 | _, ok = data["level"] 41 | if ok { 42 | data["fields.level"] = data["level"] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /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/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 logrus_airbrake 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | "github.com/tobi/airbrake-go" 6 | ) 7 | 8 | // AirbrakeHook to send exceptions to an exception-tracking service compatible 9 | // with the Airbrake API. You must set: 10 | // * airbrake.Endpoint 11 | // * airbrake.ApiKey 12 | // * airbrake.Environment (only sends exceptions when set to "production") 13 | // 14 | // Before using this hook, to send an error. Entries that trigger an Error, 15 | // Fatal or Panic should now include an "error" field to send to Airbrake. 16 | type AirbrakeHook struct{} 17 | 18 | func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error { 19 | if entry.Data["error"] == nil { 20 | entry.Logger.WithFields(logrus.Fields{ 21 | "source": "airbrake", 22 | "endpoint": airbrake.Endpoint, 23 | }).Warn("Exceptions sent to Airbrake must have an 'error' key with the error") 24 | return nil 25 | } 26 | 27 | err, ok := entry.Data["error"].(error) 28 | if !ok { 29 | entry.Logger.WithFields(logrus.Fields{ 30 | "source": "airbrake", 31 | "endpoint": airbrake.Endpoint, 32 | }).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`") 33 | return nil 34 | } 35 | 36 | airErr := airbrake.Notify(err) 37 | if airErr != nil { 38 | entry.Logger.WithFields(logrus.Fields{ 39 | "source": "airbrake", 40 | "endpoint": airbrake.Endpoint, 41 | "error": airErr, 42 | }).Warn("Failed to send error to Airbrake") 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/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 and logger. 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 | 44 | ## Timeout 45 | 46 | `Timeout` is the time the sentry hook will wait for a response 47 | from the sentry server. 48 | 49 | If this time elapses with no response from 50 | the server an error will be returned. 51 | 52 | If `Timeout` is set to 0 the SentryHook will not wait for a reply 53 | and will assume a correct delivery. 54 | 55 | The SentryHook has a default timeout of `100 milliseconds` when created 56 | with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field: 57 | 58 | ```go 59 | hook, _ := logrus_sentry.NewSentryHook(...) 60 | hook.Timeout = 20*time.Seconds 61 | ``` 62 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go: -------------------------------------------------------------------------------- 1 | package logrus_sentry 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/getsentry/raven-go" 9 | ) 10 | 11 | var ( 12 | severityMap = map[logrus.Level]raven.Severity{ 13 | logrus.DebugLevel: raven.DEBUG, 14 | logrus.InfoLevel: raven.INFO, 15 | logrus.WarnLevel: raven.WARNING, 16 | logrus.ErrorLevel: raven.ERROR, 17 | logrus.FatalLevel: raven.FATAL, 18 | logrus.PanicLevel: raven.FATAL, 19 | } 20 | ) 21 | 22 | func getAndDel(d logrus.Fields, key string) (string, bool) { 23 | var ( 24 | ok bool 25 | v interface{} 26 | val string 27 | ) 28 | if v, ok = d[key]; !ok { 29 | return "", false 30 | } 31 | 32 | if val, ok = v.(string); !ok { 33 | return "", false 34 | } 35 | delete(d, key) 36 | return val, true 37 | } 38 | 39 | // SentryHook delivers logs to a sentry server. 40 | type SentryHook struct { 41 | // Timeout sets the time to wait for a delivery error from the sentry server. 42 | // If this is set to zero the server will not wait for any response and will 43 | // consider the message correctly sent 44 | Timeout time.Duration 45 | 46 | client *raven.Client 47 | levels []logrus.Level 48 | } 49 | 50 | // NewSentryHook creates a hook to be added to an instance of logger 51 | // and initializes the raven client. 52 | // This method sets the timeout to 100 milliseconds. 53 | func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) { 54 | client, err := raven.NewClient(DSN, nil) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return &SentryHook{100 * time.Millisecond, client, levels}, nil 59 | } 60 | 61 | // Called when an event should be sent to sentry 62 | // Special fields that sentry uses to give more information to the server 63 | // are extracted from entry.Data (if they are found) 64 | // These fields are: logger and server_name 65 | func (hook *SentryHook) Fire(entry *logrus.Entry) error { 66 | packet := &raven.Packet{ 67 | Message: entry.Message, 68 | Timestamp: raven.Timestamp(entry.Time), 69 | Level: severityMap[entry.Level], 70 | Platform: "go", 71 | } 72 | 73 | d := entry.Data 74 | 75 | if logger, ok := getAndDel(d, "logger"); ok { 76 | packet.Logger = logger 77 | } 78 | if serverName, ok := getAndDel(d, "server_name"); ok { 79 | packet.ServerName = serverName 80 | } 81 | packet.Extra = map[string]interface{}(d) 82 | 83 | _, errCh := hook.client.Capture(packet, nil) 84 | timeout := hook.Timeout 85 | if timeout != 0 { 86 | timeoutCh := time.After(timeout) 87 | select { 88 | case err := <-errCh: 89 | return err 90 | case <-timeoutCh: 91 | return fmt.Errorf("no response from sentry server in %s", timeout) 92 | } 93 | } 94 | return nil 95 | } 96 | 97 | // Levels returns the available logging levels. 98 | func (hook *SentryHook) Levels() []logrus.Level { 99 | return hook.levels 100 | } 101 | -------------------------------------------------------------------------------- /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 | logger.WithFields(logrus.Fields{ 65 | "server_name": server_name, 66 | "logger": logger_name, 67 | }).Error(message) 68 | 69 | packet := <-pch 70 | if packet.Logger != logger_name { 71 | t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger) 72 | } 73 | 74 | if packet.ServerName != server_name { 75 | t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName) 76 | } 77 | }) 78 | } 79 | 80 | func TestSentryHandler(t *testing.T) { 81 | WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { 82 | logger := getTestLogger() 83 | hook, err := NewSentryHook(dsn, []logrus.Level{ 84 | logrus.ErrorLevel, 85 | }) 86 | if err != nil { 87 | t.Fatal(err.Error()) 88 | } 89 | logger.Hooks.Add(hook) 90 | 91 | logger.Error(message) 92 | packet := <-pch 93 | if packet.Message != message { 94 | t.Errorf("message should have been %s, was %s", message, packet.Message) 95 | } 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /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 | "time" 7 | ) 8 | 9 | type JSONFormatter struct{} 10 | 11 | func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { 12 | data := make(Fields, len(entry.Data)+3) 13 | for k, v := range entry.Data { 14 | data[k] = v 15 | } 16 | prefixFieldClashes(data) 17 | data["time"] = entry.Time.Format(time.RFC3339) 18 | data["msg"] = entry.Message 19 | data["level"] = entry.Level.String() 20 | 21 | serialized, err := json.Marshal(data) 22 | if err != nil { 23 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 24 | } 25 | return append(serialized, '\n'), nil 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "sync" 7 | ) 8 | 9 | type Logger struct { 10 | // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a 11 | // file, or leave it default which is `os.Stdout`. You can also set this to 12 | // something more adventorous, such as logging to Kafka. 13 | Out io.Writer 14 | // Hooks for the logger instance. These allow firing events based on logging 15 | // levels and log entries. For example, to send errors to an error tracking 16 | // service, log to StatsD or dump the core on fatal errors. 17 | Hooks levelHooks 18 | // All log entries pass through the formatter before logged to Out. The 19 | // included formatters are `TextFormatter` and `JSONFormatter` for which 20 | // TextFormatter is the default. In development (when a TTY is attached) it 21 | // logs with colors, but to a file it wouldn't. You can easily implement your 22 | // own that implements the `Formatter` interface, see the `README` or included 23 | // formatters for examples. 24 | Formatter Formatter 25 | // The logging level the logger should log at. This is typically (and defaults 26 | // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be 27 | // logged. `logrus.Debug` is useful in 28 | Level Level 29 | // Used to sync writing to the log. 30 | mu sync.Mutex 31 | } 32 | 33 | // Creates a new logger. Configuration should be set by changing `Formatter`, 34 | // `Out` and `Hooks` directly on the default logger instance. You can also just 35 | // instantiate your own: 36 | // 37 | // var log = &Logger{ 38 | // Out: os.Stderr, 39 | // Formatter: new(JSONFormatter), 40 | // Hooks: make(levelHooks), 41 | // Level: logrus.DebugLevel, 42 | // } 43 | // 44 | // It's recommended to make this a global instance called `log`. 45 | func New() *Logger { 46 | return &Logger{ 47 | Out: os.Stdout, 48 | Formatter: new(TextFormatter), 49 | Hooks: make(levelHooks), 50 | Level: InfoLevel, 51 | } 52 | } 53 | 54 | // Adds a field to the log entry, note that you it doesn't log until you call 55 | // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. 56 | // Ff you want multiple fields, use `WithFields`. 57 | func (logger *Logger) WithField(key string, value interface{}) *Entry { 58 | return NewEntry(logger).WithField(key, value) 59 | } 60 | 61 | // Adds a struct of fields to the log entry. All it does is call `WithField` for 62 | // each `Field`. 63 | func (logger *Logger) WithFields(fields Fields) *Entry { 64 | return NewEntry(logger).WithFields(fields) 65 | } 66 | 67 | func (logger *Logger) Debugf(format string, args ...interface{}) { 68 | NewEntry(logger).Debugf(format, args...) 69 | } 70 | 71 | func (logger *Logger) Infof(format string, args ...interface{}) { 72 | NewEntry(logger).Infof(format, args...) 73 | } 74 | 75 | func (logger *Logger) Printf(format string, args ...interface{}) { 76 | NewEntry(logger).Printf(format, args...) 77 | } 78 | 79 | func (logger *Logger) Warnf(format string, args ...interface{}) { 80 | NewEntry(logger).Warnf(format, args...) 81 | } 82 | 83 | func (logger *Logger) Warningf(format string, args ...interface{}) { 84 | NewEntry(logger).Warnf(format, args...) 85 | } 86 | 87 | func (logger *Logger) Errorf(format string, args ...interface{}) { 88 | NewEntry(logger).Errorf(format, args...) 89 | } 90 | 91 | func (logger *Logger) Fatalf(format string, args ...interface{}) { 92 | NewEntry(logger).Fatalf(format, args...) 93 | } 94 | 95 | func (logger *Logger) Panicf(format string, args ...interface{}) { 96 | NewEntry(logger).Panicf(format, args...) 97 | } 98 | 99 | func (logger *Logger) Debug(args ...interface{}) { 100 | NewEntry(logger).Debug(args...) 101 | } 102 | 103 | func (logger *Logger) Info(args ...interface{}) { 104 | NewEntry(logger).Info(args...) 105 | } 106 | 107 | func (logger *Logger) Print(args ...interface{}) { 108 | NewEntry(logger).Info(args...) 109 | } 110 | 111 | func (logger *Logger) Warn(args ...interface{}) { 112 | NewEntry(logger).Warn(args...) 113 | } 114 | 115 | func (logger *Logger) Warning(args ...interface{}) { 116 | NewEntry(logger).Warn(args...) 117 | } 118 | 119 | func (logger *Logger) Error(args ...interface{}) { 120 | NewEntry(logger).Error(args...) 121 | } 122 | 123 | func (logger *Logger) Fatal(args ...interface{}) { 124 | NewEntry(logger).Fatal(args...) 125 | } 126 | 127 | func (logger *Logger) Panic(args ...interface{}) { 128 | NewEntry(logger).Panic(args...) 129 | } 130 | 131 | func (logger *Logger) Debugln(args ...interface{}) { 132 | NewEntry(logger).Debugln(args...) 133 | } 134 | 135 | func (logger *Logger) Infoln(args ...interface{}) { 136 | NewEntry(logger).Infoln(args...) 137 | } 138 | 139 | func (logger *Logger) Println(args ...interface{}) { 140 | NewEntry(logger).Println(args...) 141 | } 142 | 143 | func (logger *Logger) Warnln(args ...interface{}) { 144 | NewEntry(logger).Warnln(args...) 145 | } 146 | 147 | func (logger *Logger) Warningln(args ...interface{}) { 148 | NewEntry(logger).Warnln(args...) 149 | } 150 | 151 | func (logger *Logger) Errorln(args ...interface{}) { 152 | NewEntry(logger).Errorln(args...) 153 | } 154 | 155 | func (logger *Logger) Fatalln(args ...interface{}) { 156 | NewEntry(logger).Fatalln(args...) 157 | } 158 | 159 | func (logger *Logger) Panicln(args ...interface{}) { 160 | NewEntry(logger).Panicln(args...) 161 | } 162 | -------------------------------------------------------------------------------- /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/logrus_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "strconv" 7 | "strings" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { 14 | var buffer bytes.Buffer 15 | var fields Fields 16 | 17 | logger := New() 18 | logger.Out = &buffer 19 | logger.Formatter = new(JSONFormatter) 20 | 21 | log(logger) 22 | 23 | err := json.Unmarshal(buffer.Bytes(), &fields) 24 | assert.Nil(t, err) 25 | 26 | assertions(fields) 27 | } 28 | 29 | func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { 30 | var buffer bytes.Buffer 31 | 32 | logger := New() 33 | logger.Out = &buffer 34 | logger.Formatter = &TextFormatter{ 35 | DisableColors: true, 36 | } 37 | 38 | log(logger) 39 | 40 | fields := make(map[string]string) 41 | for _, kv := range strings.Split(buffer.String(), " ") { 42 | if !strings.Contains(kv, "=") { 43 | continue 44 | } 45 | kvArr := strings.Split(kv, "=") 46 | key := strings.TrimSpace(kvArr[0]) 47 | val := kvArr[1] 48 | if kvArr[1][0] == '"' { 49 | var err error 50 | val, err = strconv.Unquote(val) 51 | assert.NoError(t, err) 52 | } 53 | fields[key] = val 54 | } 55 | assertions(fields) 56 | } 57 | 58 | func TestPrint(t *testing.T) { 59 | LogAndAssertJSON(t, func(log *Logger) { 60 | log.Print("test") 61 | }, func(fields Fields) { 62 | assert.Equal(t, fields["msg"], "test") 63 | assert.Equal(t, fields["level"], "info") 64 | }) 65 | } 66 | 67 | func TestInfo(t *testing.T) { 68 | LogAndAssertJSON(t, func(log *Logger) { 69 | log.Info("test") 70 | }, func(fields Fields) { 71 | assert.Equal(t, fields["msg"], "test") 72 | assert.Equal(t, fields["level"], "info") 73 | }) 74 | } 75 | 76 | func TestWarn(t *testing.T) { 77 | LogAndAssertJSON(t, func(log *Logger) { 78 | log.Warn("test") 79 | }, func(fields Fields) { 80 | assert.Equal(t, fields["msg"], "test") 81 | assert.Equal(t, fields["level"], "warning") 82 | }) 83 | } 84 | 85 | func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { 86 | LogAndAssertJSON(t, func(log *Logger) { 87 | log.Infoln("test", "test") 88 | }, func(fields Fields) { 89 | assert.Equal(t, fields["msg"], "test test") 90 | }) 91 | } 92 | 93 | func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { 94 | LogAndAssertJSON(t, func(log *Logger) { 95 | log.Infoln("test", 10) 96 | }, func(fields Fields) { 97 | assert.Equal(t, fields["msg"], "test 10") 98 | }) 99 | } 100 | 101 | func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { 102 | LogAndAssertJSON(t, func(log *Logger) { 103 | log.Infoln(10, 10) 104 | }, func(fields Fields) { 105 | assert.Equal(t, fields["msg"], "10 10") 106 | }) 107 | } 108 | 109 | func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { 110 | LogAndAssertJSON(t, func(log *Logger) { 111 | log.Infoln(10, 10) 112 | }, func(fields Fields) { 113 | assert.Equal(t, fields["msg"], "10 10") 114 | }) 115 | } 116 | 117 | func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { 118 | LogAndAssertJSON(t, func(log *Logger) { 119 | log.Info("test", 10) 120 | }, func(fields Fields) { 121 | assert.Equal(t, fields["msg"], "test10") 122 | }) 123 | } 124 | 125 | func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { 126 | LogAndAssertJSON(t, func(log *Logger) { 127 | log.Info("test", "test") 128 | }, func(fields Fields) { 129 | assert.Equal(t, fields["msg"], "testtest") 130 | }) 131 | } 132 | 133 | func TestWithFieldsShouldAllowAssignments(t *testing.T) { 134 | var buffer bytes.Buffer 135 | var fields Fields 136 | 137 | logger := New() 138 | logger.Out = &buffer 139 | logger.Formatter = new(JSONFormatter) 140 | 141 | localLog := logger.WithFields(Fields{ 142 | "key1": "value1", 143 | }) 144 | 145 | localLog.WithField("key2", "value2").Info("test") 146 | err := json.Unmarshal(buffer.Bytes(), &fields) 147 | assert.Nil(t, err) 148 | 149 | assert.Equal(t, "value2", fields["key2"]) 150 | assert.Equal(t, "value1", fields["key1"]) 151 | 152 | buffer = bytes.Buffer{} 153 | fields = Fields{} 154 | localLog.Info("test") 155 | err = json.Unmarshal(buffer.Bytes(), &fields) 156 | assert.Nil(t, err) 157 | 158 | _, ok := fields["key2"] 159 | assert.Equal(t, false, ok) 160 | assert.Equal(t, "value1", fields["key1"]) 161 | } 162 | 163 | func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { 164 | LogAndAssertJSON(t, func(log *Logger) { 165 | log.WithField("msg", "hello").Info("test") 166 | }, func(fields Fields) { 167 | assert.Equal(t, fields["msg"], "test") 168 | }) 169 | } 170 | 171 | func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { 172 | LogAndAssertJSON(t, func(log *Logger) { 173 | log.WithField("msg", "hello").Info("test") 174 | }, func(fields Fields) { 175 | assert.Equal(t, fields["msg"], "test") 176 | assert.Equal(t, fields["fields.msg"], "hello") 177 | }) 178 | } 179 | 180 | func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { 181 | LogAndAssertJSON(t, func(log *Logger) { 182 | log.WithField("time", "hello").Info("test") 183 | }, func(fields Fields) { 184 | assert.Equal(t, fields["fields.time"], "hello") 185 | }) 186 | } 187 | 188 | func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { 189 | LogAndAssertJSON(t, func(log *Logger) { 190 | log.WithField("level", 1).Info("test") 191 | }, func(fields Fields) { 192 | assert.Equal(t, fields["level"], "info") 193 | assert.Equal(t, fields["fields.level"], 1) 194 | }) 195 | } 196 | 197 | func TestDefaultFieldsAreNotPrefixed(t *testing.T) { 198 | LogAndAssertText(t, func(log *Logger) { 199 | ll := log.WithField("herp", "derp") 200 | ll.Info("hello") 201 | ll.Info("bye") 202 | }, func(fields map[string]string) { 203 | for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} { 204 | if _, ok := fields[fieldName]; ok { 205 | t.Fatalf("should not have prefixed %q: %v", fieldName, fields) 206 | } 207 | } 208 | }) 209 | } 210 | 211 | func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { 212 | 213 | var buffer bytes.Buffer 214 | var fields Fields 215 | 216 | logger := New() 217 | logger.Out = &buffer 218 | logger.Formatter = new(JSONFormatter) 219 | 220 | llog := logger.WithField("context", "eating raw fish") 221 | 222 | llog.Info("looks delicious") 223 | 224 | err := json.Unmarshal(buffer.Bytes(), &fields) 225 | assert.NoError(t, err, "should have decoded first message") 226 | assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") 227 | assert.Equal(t, fields["msg"], "looks delicious") 228 | assert.Equal(t, fields["context"], "eating raw fish") 229 | 230 | buffer.Reset() 231 | 232 | llog.Warn("omg it is!") 233 | 234 | err = json.Unmarshal(buffer.Bytes(), &fields) 235 | assert.NoError(t, err, "should have decoded second message") 236 | assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") 237 | assert.Equal(t, fields["msg"], "omg it is!") 238 | assert.Equal(t, fields["context"], "eating raw fish") 239 | assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") 240 | 241 | } 242 | 243 | func TestConvertLevelToString(t *testing.T) { 244 | assert.Equal(t, "debug", DebugLevel.String()) 245 | assert.Equal(t, "info", InfoLevel.String()) 246 | assert.Equal(t, "warning", WarnLevel.String()) 247 | assert.Equal(t, "error", ErrorLevel.String()) 248 | assert.Equal(t, "fatal", FatalLevel.String()) 249 | assert.Equal(t, "panic", PanicLevel.String()) 250 | } 251 | 252 | func TestParseLevel(t *testing.T) { 253 | l, err := ParseLevel("panic") 254 | assert.Nil(t, err) 255 | assert.Equal(t, PanicLevel, l) 256 | 257 | l, err = ParseLevel("fatal") 258 | assert.Nil(t, err) 259 | assert.Equal(t, FatalLevel, l) 260 | 261 | l, err = ParseLevel("error") 262 | assert.Nil(t, err) 263 | assert.Equal(t, ErrorLevel, l) 264 | 265 | l, err = ParseLevel("warn") 266 | assert.Nil(t, err) 267 | assert.Equal(t, WarnLevel, l) 268 | 269 | l, err = ParseLevel("warning") 270 | assert.Nil(t, err) 271 | assert.Equal(t, WarnLevel, l) 272 | 273 | l, err = ParseLevel("info") 274 | assert.Nil(t, err) 275 | assert.Equal(t, InfoLevel, l) 276 | 277 | l, err = ParseLevel("debug") 278 | assert.Nil(t, err) 279 | assert.Equal(t, DebugLevel, l) 280 | 281 | l, err = ParseLevel("invalid") 282 | assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) 283 | } 284 | -------------------------------------------------------------------------------- /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,!appengine 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 | 2 | package logrus 3 | 4 | import "syscall" 5 | 6 | const ioctlReadTermios = syscall.TIOCGETA 7 | 8 | type Termios syscall.Termios 9 | -------------------------------------------------------------------------------- /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 | "regexp" 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 | ) 19 | 20 | var ( 21 | baseTimestamp time.Time 22 | isTerminal bool 23 | noQuoteNeeded *regexp.Regexp 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 | DisableColors bool 39 | // Set to true to disable timestamp logging (useful when the output 40 | // is redirected to a logging system already adding a timestamp) 41 | DisableTimestamp bool 42 | } 43 | 44 | func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { 45 | 46 | var keys []string 47 | for k := range entry.Data { 48 | keys = append(keys, k) 49 | } 50 | sort.Strings(keys) 51 | 52 | b := &bytes.Buffer{} 53 | 54 | prefixFieldClashes(entry.Data) 55 | 56 | isColored := (f.ForceColors || isTerminal) && !f.DisableColors 57 | 58 | if isColored { 59 | printColored(b, entry, keys) 60 | } else { 61 | if !f.DisableTimestamp { 62 | f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339)) 63 | } 64 | f.appendKeyValue(b, "level", entry.Level.String()) 65 | f.appendKeyValue(b, "msg", entry.Message) 66 | for _, key := range keys { 67 | f.appendKeyValue(b, key, entry.Data[key]) 68 | } 69 | } 70 | 71 | b.WriteByte('\n') 72 | return b.Bytes(), nil 73 | } 74 | 75 | func printColored(b *bytes.Buffer, entry *Entry, keys []string) { 76 | var levelColor int 77 | switch entry.Level { 78 | case WarnLevel: 79 | levelColor = yellow 80 | case ErrorLevel, FatalLevel, PanicLevel: 81 | levelColor = red 82 | default: 83 | levelColor = blue 84 | } 85 | 86 | levelText := strings.ToUpper(entry.Level.String())[0:4] 87 | 88 | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) 89 | for _, k := range keys { 90 | v := entry.Data[k] 91 | fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) 92 | } 93 | } 94 | 95 | func needsQuoting(text string) bool { 96 | for _, ch := range text { 97 | if !((ch >= 'a' && ch <= 'z') || 98 | (ch >= 'A' && ch <= 'Z') || 99 | (ch >= '0' && ch < '9') || 100 | ch == '-' || ch == '.') { 101 | return false 102 | } 103 | } 104 | return true 105 | } 106 | 107 | func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { 108 | switch value.(type) { 109 | case string: 110 | if needsQuoting(value.(string)) { 111 | fmt.Fprintf(b, "%v=%s ", key, value) 112 | } else { 113 | fmt.Fprintf(b, "%v=%q ", key, value) 114 | } 115 | case error: 116 | if needsQuoting(value.(error).Error()) { 117 | fmt.Fprintf(b, "%v=%s ", key, value) 118 | } else { 119 | fmt.Fprintf(b, "%v=%q ", key, value) 120 | } 121 | default: 122 | fmt.Fprintf(b, "%v=%v ", key, value) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | 7 | "testing" 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(true, "/foobar") 29 | checkQuoting(true, "x y") 30 | checkQuoting(true, "x,y") 31 | checkQuoting(false, errors.New("invalid")) 32 | checkQuoting(true, errors.New("invalid argument")) 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/README.md: -------------------------------------------------------------------------------- 1 | # Go's `text/template` package with newline elision 2 | 3 | This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline. 4 | 5 | eg. 6 | 7 | ``` 8 | {{if true}}\ 9 | hello 10 | {{end}}\ 11 | ``` 12 | 13 | Will result in: 14 | 15 | ``` 16 | hello\n 17 | ``` 18 | 19 | Rather than: 20 | 21 | ``` 22 | \n 23 | hello\n 24 | \n 25 | ``` 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 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 | package template_test 6 | 7 | import ( 8 | "log" 9 | "os" 10 | "github.com/alecthomas/template" 11 | ) 12 | 13 | func ExampleTemplate() { 14 | // Define a template. 15 | const letter = ` 16 | Dear {{.Name}}, 17 | {{if .Attended}} 18 | It was a pleasure to see you at the wedding.{{else}} 19 | It is a shame you couldn't make it to the wedding.{{end}} 20 | {{with .Gift}}Thank you for the lovely {{.}}. 21 | {{end}} 22 | Best wishes, 23 | Josie 24 | ` 25 | 26 | // Prepare some data to insert into the template. 27 | type Recipient struct { 28 | Name, Gift string 29 | Attended bool 30 | } 31 | var recipients = []Recipient{ 32 | {"Aunt Mildred", "bone china tea set", true}, 33 | {"Uncle John", "moleskin pants", false}, 34 | {"Cousin Rodney", "", false}, 35 | } 36 | 37 | // Create a new template and parse the letter into it. 38 | t := template.Must(template.New("letter").Parse(letter)) 39 | 40 | // Execute the template for each recipient. 41 | for _, r := range recipients { 42 | err := t.Execute(os.Stdout, r) 43 | if err != nil { 44 | log.Println("executing template:", err) 45 | } 46 | } 47 | 48 | // Output: 49 | // Dear Aunt Mildred, 50 | // 51 | // It was a pleasure to see you at the wedding. 52 | // Thank you for the lovely bone china tea set. 53 | // 54 | // Best wishes, 55 | // Josie 56 | // 57 | // Dear Uncle John, 58 | // 59 | // It is a shame you couldn't make it to the wedding. 60 | // Thank you for the lovely moleskin pants. 61 | // 62 | // Best wishes, 63 | // Josie 64 | // 65 | // Dear Cousin Rodney, 66 | // 67 | // It is a shame you couldn't make it to the wedding. 68 | // 69 | // Best wishes, 70 | // Josie 71 | } 72 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/examplefiles_test.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 | package template_test 6 | 7 | import ( 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "path/filepath" 13 | "github.com/alecthomas/template" 14 | ) 15 | 16 | // templateFile defines the contents of a template to be stored in a file, for testing. 17 | type templateFile struct { 18 | name string 19 | contents string 20 | } 21 | 22 | func createTestDir(files []templateFile) string { 23 | dir, err := ioutil.TempDir("", "template") 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | for _, file := range files { 28 | f, err := os.Create(filepath.Join(dir, file.name)) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | defer f.Close() 33 | _, err = io.WriteString(f, file.contents) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | } 38 | return dir 39 | } 40 | 41 | // Here we demonstrate loading a set of templates from a directory. 42 | func ExampleTemplate_glob() { 43 | // Here we create a temporary directory and populate it with our sample 44 | // template definition files; usually the template files would already 45 | // exist in some location known to the program. 46 | dir := createTestDir([]templateFile{ 47 | // T0.tmpl is a plain template file that just invokes T1. 48 | {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`}, 49 | // T1.tmpl defines a template, T1 that invokes T2. 50 | {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, 51 | // T2.tmpl defines a template T2. 52 | {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, 53 | }) 54 | // Clean up after the test; another quirk of running as an example. 55 | defer os.RemoveAll(dir) 56 | 57 | // pattern is the glob pattern used to find all the template files. 58 | pattern := filepath.Join(dir, "*.tmpl") 59 | 60 | // Here starts the example proper. 61 | // T0.tmpl is the first name matched, so it becomes the starting template, 62 | // the value returned by ParseGlob. 63 | tmpl := template.Must(template.ParseGlob(pattern)) 64 | 65 | err := tmpl.Execute(os.Stdout, nil) 66 | if err != nil { 67 | log.Fatalf("template execution: %s", err) 68 | } 69 | // Output: 70 | // T0 invokes T1: (T1 invokes T2: (This is T2)) 71 | } 72 | 73 | // This example demonstrates one way to share some templates 74 | // and use them in different contexts. In this variant we add multiple driver 75 | // templates by hand to an existing bundle of templates. 76 | func ExampleTemplate_helpers() { 77 | // Here we create a temporary directory and populate it with our sample 78 | // template definition files; usually the template files would already 79 | // exist in some location known to the program. 80 | dir := createTestDir([]templateFile{ 81 | // T1.tmpl defines a template, T1 that invokes T2. 82 | {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, 83 | // T2.tmpl defines a template T2. 84 | {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, 85 | }) 86 | // Clean up after the test; another quirk of running as an example. 87 | defer os.RemoveAll(dir) 88 | 89 | // pattern is the glob pattern used to find all the template files. 90 | pattern := filepath.Join(dir, "*.tmpl") 91 | 92 | // Here starts the example proper. 93 | // Load the helpers. 94 | templates := template.Must(template.ParseGlob(pattern)) 95 | // Add one driver template to the bunch; we do this with an explicit template definition. 96 | _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}") 97 | if err != nil { 98 | log.Fatal("parsing driver1: ", err) 99 | } 100 | // Add another driver template. 101 | _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}") 102 | if err != nil { 103 | log.Fatal("parsing driver2: ", err) 104 | } 105 | // We load all the templates before execution. This package does not require 106 | // that behavior but html/template's escaping does, so it's a good habit. 107 | err = templates.ExecuteTemplate(os.Stdout, "driver1", nil) 108 | if err != nil { 109 | log.Fatalf("driver1 execution: %s", err) 110 | } 111 | err = templates.ExecuteTemplate(os.Stdout, "driver2", nil) 112 | if err != nil { 113 | log.Fatalf("driver2 execution: %s", err) 114 | } 115 | // Output: 116 | // Driver 1 calls T1: (T1 invokes T2: (This is T2)) 117 | // Driver 2 calls T2: (This is T2) 118 | } 119 | 120 | // This example demonstrates how to use one group of driver 121 | // templates with distinct sets of helper templates. 122 | func ExampleTemplate_share() { 123 | // Here we create a temporary directory and populate it with our sample 124 | // template definition files; usually the template files would already 125 | // exist in some location known to the program. 126 | dir := createTestDir([]templateFile{ 127 | // T0.tmpl is a plain template file that just invokes T1. 128 | {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"}, 129 | // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined 130 | {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, 131 | }) 132 | // Clean up after the test; another quirk of running as an example. 133 | defer os.RemoveAll(dir) 134 | 135 | // pattern is the glob pattern used to find all the template files. 136 | pattern := filepath.Join(dir, "*.tmpl") 137 | 138 | // Here starts the example proper. 139 | // Load the drivers. 140 | drivers := template.Must(template.ParseGlob(pattern)) 141 | 142 | // We must define an implementation of the T2 template. First we clone 143 | // the drivers, then add a definition of T2 to the template name space. 144 | 145 | // 1. Clone the helper set to create a new name space from which to run them. 146 | first, err := drivers.Clone() 147 | if err != nil { 148 | log.Fatal("cloning helpers: ", err) 149 | } 150 | // 2. Define T2, version A, and parse it. 151 | _, err = first.Parse("{{define `T2`}}T2, version A{{end}}") 152 | if err != nil { 153 | log.Fatal("parsing T2: ", err) 154 | } 155 | 156 | // Now repeat the whole thing, using a different version of T2. 157 | // 1. Clone the drivers. 158 | second, err := drivers.Clone() 159 | if err != nil { 160 | log.Fatal("cloning drivers: ", err) 161 | } 162 | // 2. Define T2, version B, and parse it. 163 | _, err = second.Parse("{{define `T2`}}T2, version B{{end}}") 164 | if err != nil { 165 | log.Fatal("parsing T2: ", err) 166 | } 167 | 168 | // Execute the templates in the reverse order to verify the 169 | // first is unaffected by the second. 170 | err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second") 171 | if err != nil { 172 | log.Fatalf("second execution: %s", err) 173 | } 174 | err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first") 175 | if err != nil { 176 | log.Fatalf("first: execution: %s", err) 177 | } 178 | 179 | // Output: 180 | // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B)) 181 | // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A)) 182 | } 183 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/examplefunc_test.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 | package template_test 6 | 7 | import ( 8 | "log" 9 | "os" 10 | "strings" 11 | "github.com/alecthomas/template" 12 | ) 13 | 14 | // This example demonstrates a custom function to process template text. 15 | // It installs the strings.Title function and uses it to 16 | // Make Title Text Look Good In Our Template's Output. 17 | func ExampleTemplate_func() { 18 | // First we create a FuncMap with which to register the function. 19 | funcMap := template.FuncMap{ 20 | // The name "title" is what the function will be called in the template text. 21 | "title": strings.Title, 22 | } 23 | 24 | // A simple template definition to test our function. 25 | // We print the input text several ways: 26 | // - the original 27 | // - title-cased 28 | // - title-cased and then printed with %q 29 | // - printed with %q and then title-cased. 30 | const templateText = ` 31 | Input: {{printf "%q" .}} 32 | Output 0: {{title .}} 33 | Output 1: {{title . | printf "%q"}} 34 | Output 2: {{printf "%q" . | title}} 35 | ` 36 | 37 | // Create a template, add the function map, and parse the text. 38 | tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText) 39 | if err != nil { 40 | log.Fatalf("parsing: %s", err) 41 | } 42 | 43 | // Run the template to verify the output. 44 | err = tmpl.Execute(os.Stdout, "the go programming language") 45 | if err != nil { 46 | log.Fatalf("execution: %s", err) 47 | } 48 | 49 | // Output: 50 | // Input: "the go programming language" 51 | // Output 0: The Go Programming Language 52 | // Output 1: "The Go Programming Language" 53 | // Output 2: "The Go Programming Language" 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/helper.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 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 | // Helper functions to make constructing templates easier. 6 | 7 | package template 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "path/filepath" 13 | ) 14 | 15 | // Functions and methods to parse templates. 16 | 17 | // Must is a helper that wraps a call to a function returning (*Template, error) 18 | // and panics if the error is non-nil. It is intended for use in variable 19 | // initializations such as 20 | // var t = template.Must(template.New("name").Parse("text")) 21 | func Must(t *Template, err error) *Template { 22 | if err != nil { 23 | panic(err) 24 | } 25 | return t 26 | } 27 | 28 | // ParseFiles creates a new Template and parses the template definitions from 29 | // the named files. The returned template's name will have the (base) name and 30 | // (parsed) contents of the first file. There must be at least one file. 31 | // If an error occurs, parsing stops and the returned *Template is nil. 32 | func ParseFiles(filenames ...string) (*Template, error) { 33 | return parseFiles(nil, filenames...) 34 | } 35 | 36 | // ParseFiles parses the named files and associates the resulting templates with 37 | // t. If an error occurs, parsing stops and the returned template is nil; 38 | // otherwise it is t. There must be at least one file. 39 | func (t *Template) ParseFiles(filenames ...string) (*Template, error) { 40 | return parseFiles(t, filenames...) 41 | } 42 | 43 | // parseFiles is the helper for the method and function. If the argument 44 | // template is nil, it is created from the first file. 45 | func parseFiles(t *Template, filenames ...string) (*Template, error) { 46 | if len(filenames) == 0 { 47 | // Not really a problem, but be consistent. 48 | return nil, fmt.Errorf("template: no files named in call to ParseFiles") 49 | } 50 | for _, filename := range filenames { 51 | b, err := ioutil.ReadFile(filename) 52 | if err != nil { 53 | return nil, err 54 | } 55 | s := string(b) 56 | name := filepath.Base(filename) 57 | // First template becomes return value if not already defined, 58 | // and we use that one for subsequent New calls to associate 59 | // all the templates together. Also, if this file has the same name 60 | // as t, this file becomes the contents of t, so 61 | // t, err := New(name).Funcs(xxx).ParseFiles(name) 62 | // works. Otherwise we create a new template associated with t. 63 | var tmpl *Template 64 | if t == nil { 65 | t = New(name) 66 | } 67 | if name == t.Name() { 68 | tmpl = t 69 | } else { 70 | tmpl = t.New(name) 71 | } 72 | _, err = tmpl.Parse(s) 73 | if err != nil { 74 | return nil, err 75 | } 76 | } 77 | return t, nil 78 | } 79 | 80 | // ParseGlob creates a new Template and parses the template definitions from the 81 | // files identified by the pattern, which must match at least one file. The 82 | // returned template will have the (base) name and (parsed) contents of the 83 | // first file matched by the pattern. ParseGlob is equivalent to calling 84 | // ParseFiles with the list of files matched by the pattern. 85 | func ParseGlob(pattern string) (*Template, error) { 86 | return parseGlob(nil, pattern) 87 | } 88 | 89 | // ParseGlob parses the template definitions in the files identified by the 90 | // pattern and associates the resulting templates with t. The pattern is 91 | // processed by filepath.Glob and must match at least one file. ParseGlob is 92 | // equivalent to calling t.ParseFiles with the list of files matched by the 93 | // pattern. 94 | func (t *Template) ParseGlob(pattern string) (*Template, error) { 95 | return parseGlob(t, pattern) 96 | } 97 | 98 | // parseGlob is the implementation of the function and method ParseGlob. 99 | func parseGlob(t *Template, pattern string) (*Template, error) { 100 | filenames, err := filepath.Glob(pattern) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if len(filenames) == 0 { 105 | return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) 106 | } 107 | return parseFiles(t, filenames...) 108 | } 109 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/multi_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 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 | package template 6 | 7 | // Tests for mulitple-template parsing and execution. 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "strings" 13 | "testing" 14 | "github.com/alecthomas/template/parse" 15 | ) 16 | 17 | const ( 18 | noError = true 19 | hasError = false 20 | ) 21 | 22 | type multiParseTest struct { 23 | name string 24 | input string 25 | ok bool 26 | names []string 27 | results []string 28 | } 29 | 30 | var multiParseTests = []multiParseTest{ 31 | {"empty", "", noError, 32 | nil, 33 | nil}, 34 | {"one", `{{define "foo"}} FOO {{end}}`, noError, 35 | []string{"foo"}, 36 | []string{" FOO "}}, 37 | {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, 38 | []string{"foo", "bar"}, 39 | []string{" FOO ", " BAR "}}, 40 | // errors 41 | {"missing end", `{{define "foo"}} FOO `, hasError, 42 | nil, 43 | nil}, 44 | {"malformed name", `{{define "foo}} FOO `, hasError, 45 | nil, 46 | nil}, 47 | } 48 | 49 | func TestMultiParse(t *testing.T) { 50 | for _, test := range multiParseTests { 51 | template, err := New("root").Parse(test.input) 52 | switch { 53 | case err == nil && !test.ok: 54 | t.Errorf("%q: expected error; got none", test.name) 55 | continue 56 | case err != nil && test.ok: 57 | t.Errorf("%q: unexpected error: %v", test.name, err) 58 | continue 59 | case err != nil && !test.ok: 60 | // expected error, got one 61 | if *debug { 62 | fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) 63 | } 64 | continue 65 | } 66 | if template == nil { 67 | continue 68 | } 69 | if len(template.tmpl) != len(test.names)+1 { // +1 for root 70 | t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl)) 71 | continue 72 | } 73 | for i, name := range test.names { 74 | tmpl, ok := template.tmpl[name] 75 | if !ok { 76 | t.Errorf("%s: can't find template %q", test.name, name) 77 | continue 78 | } 79 | result := tmpl.Root.String() 80 | if result != test.results[i] { 81 | t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) 82 | } 83 | } 84 | } 85 | } 86 | 87 | var multiExecTests = []execTest{ 88 | {"empty", "", "", nil, true}, 89 | {"text", "some text", "some text", nil, true}, 90 | {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, 91 | {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, 92 | {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, 93 | {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, 94 | {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, 95 | {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, 96 | {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, 97 | 98 | // User-defined function: test argument evaluator. 99 | {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, 100 | {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, 101 | } 102 | 103 | // These strings are also in testdata/*. 104 | const multiText1 = ` 105 | {{define "x"}}TEXT{{end}} 106 | {{define "dotV"}}{{.V}}{{end}} 107 | ` 108 | 109 | const multiText2 = ` 110 | {{define "dot"}}{{.}}{{end}} 111 | {{define "nested"}}{{template "dot" .}}{{end}} 112 | ` 113 | 114 | func TestMultiExecute(t *testing.T) { 115 | // Declare a couple of templates first. 116 | template, err := New("root").Parse(multiText1) 117 | if err != nil { 118 | t.Fatalf("parse error for 1: %s", err) 119 | } 120 | _, err = template.Parse(multiText2) 121 | if err != nil { 122 | t.Fatalf("parse error for 2: %s", err) 123 | } 124 | testExecute(multiExecTests, template, t) 125 | } 126 | 127 | func TestParseFiles(t *testing.T) { 128 | _, err := ParseFiles("DOES NOT EXIST") 129 | if err == nil { 130 | t.Error("expected error for non-existent file; got none") 131 | } 132 | template := New("root") 133 | _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") 134 | if err != nil { 135 | t.Fatalf("error parsing files: %v", err) 136 | } 137 | testExecute(multiExecTests, template, t) 138 | } 139 | 140 | func TestParseGlob(t *testing.T) { 141 | _, err := ParseGlob("DOES NOT EXIST") 142 | if err == nil { 143 | t.Error("expected error for non-existent file; got none") 144 | } 145 | _, err = New("error").ParseGlob("[x") 146 | if err == nil { 147 | t.Error("expected error for bad pattern; got none") 148 | } 149 | template := New("root") 150 | _, err = template.ParseGlob("testdata/file*.tmpl") 151 | if err != nil { 152 | t.Fatalf("error parsing files: %v", err) 153 | } 154 | testExecute(multiExecTests, template, t) 155 | } 156 | 157 | // In these tests, actual content (not just template definitions) comes from the parsed files. 158 | 159 | var templateFileExecTests = []execTest{ 160 | {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true}, 161 | } 162 | 163 | func TestParseFilesWithData(t *testing.T) { 164 | template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") 165 | if err != nil { 166 | t.Fatalf("error parsing files: %v", err) 167 | } 168 | testExecute(templateFileExecTests, template, t) 169 | } 170 | 171 | func TestParseGlobWithData(t *testing.T) { 172 | template, err := New("root").ParseGlob("testdata/tmpl*.tmpl") 173 | if err != nil { 174 | t.Fatalf("error parsing files: %v", err) 175 | } 176 | testExecute(templateFileExecTests, template, t) 177 | } 178 | 179 | const ( 180 | cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}` 181 | cloneText2 = `{{define "b"}}b{{end}}` 182 | cloneText3 = `{{define "c"}}root{{end}}` 183 | cloneText4 = `{{define "c"}}clone{{end}}` 184 | ) 185 | 186 | func TestClone(t *testing.T) { 187 | // Create some templates and clone the root. 188 | root, err := New("root").Parse(cloneText1) 189 | if err != nil { 190 | t.Fatal(err) 191 | } 192 | _, err = root.Parse(cloneText2) 193 | if err != nil { 194 | t.Fatal(err) 195 | } 196 | clone := Must(root.Clone()) 197 | // Add variants to both. 198 | _, err = root.Parse(cloneText3) 199 | if err != nil { 200 | t.Fatal(err) 201 | } 202 | _, err = clone.Parse(cloneText4) 203 | if err != nil { 204 | t.Fatal(err) 205 | } 206 | // Verify that the clone is self-consistent. 207 | for k, v := range clone.tmpl { 208 | if k == clone.name && v.tmpl[k] != clone { 209 | t.Error("clone does not contain root") 210 | } 211 | if v != v.tmpl[v.name] { 212 | t.Errorf("clone does not contain self for %q", k) 213 | } 214 | } 215 | // Execute root. 216 | var b bytes.Buffer 217 | err = root.ExecuteTemplate(&b, "a", 0) 218 | if err != nil { 219 | t.Fatal(err) 220 | } 221 | if b.String() != "broot" { 222 | t.Errorf("expected %q got %q", "broot", b.String()) 223 | } 224 | // Execute copy. 225 | b.Reset() 226 | err = clone.ExecuteTemplate(&b, "a", 0) 227 | if err != nil { 228 | t.Fatal(err) 229 | } 230 | if b.String() != "bclone" { 231 | t.Errorf("expected %q got %q", "bclone", b.String()) 232 | } 233 | } 234 | 235 | func TestAddParseTree(t *testing.T) { 236 | // Create some templates. 237 | root, err := New("root").Parse(cloneText1) 238 | if err != nil { 239 | t.Fatal(err) 240 | } 241 | _, err = root.Parse(cloneText2) 242 | if err != nil { 243 | t.Fatal(err) 244 | } 245 | // Add a new parse tree. 246 | tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins) 247 | if err != nil { 248 | t.Fatal(err) 249 | } 250 | added, err := root.AddParseTree("c", tree["c"]) 251 | // Execute. 252 | var b bytes.Buffer 253 | err = added.ExecuteTemplate(&b, "a", 0) 254 | if err != nil { 255 | t.Fatal(err) 256 | } 257 | if b.String() != "broot" { 258 | t.Errorf("expected %q got %q", "broot", b.String()) 259 | } 260 | } 261 | 262 | // Issue 7032 263 | func TestAddParseTreeToUnparsedTemplate(t *testing.T) { 264 | master := "{{define \"master\"}}{{end}}" 265 | tmpl := New("master") 266 | tree, err := parse.Parse("master", master, "", "", nil) 267 | if err != nil { 268 | t.Fatalf("unexpected parse err: %v", err) 269 | } 270 | masterTree := tree["master"] 271 | tmpl.AddParseTree("master", masterTree) // used to panic 272 | } 273 | 274 | func TestRedefinition(t *testing.T) { 275 | var tmpl *Template 276 | var err error 277 | if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil { 278 | t.Fatalf("parse 1: %v", err) 279 | } 280 | if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil { 281 | t.Fatal("expected error") 282 | } 283 | if !strings.Contains(err.Error(), "redefinition") { 284 | t.Fatalf("expected redefinition error; got %v", err) 285 | } 286 | if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil { 287 | t.Fatal("expected error") 288 | } 289 | if !strings.Contains(err.Error(), "redefinition") { 290 | t.Fatalf("expected redefinition error; got %v", err) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/template.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 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 | package template 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | "github.com/alecthomas/template/parse" 11 | ) 12 | 13 | // common holds the information shared by related templates. 14 | type common struct { 15 | tmpl map[string]*Template 16 | // We use two maps, one for parsing and one for execution. 17 | // This separation makes the API cleaner since it doesn't 18 | // expose reflection to the client. 19 | parseFuncs FuncMap 20 | execFuncs map[string]reflect.Value 21 | } 22 | 23 | // Template is the representation of a parsed template. The *parse.Tree 24 | // field is exported only for use by html/template and should be treated 25 | // as unexported by all other clients. 26 | type Template struct { 27 | name string 28 | *parse.Tree 29 | *common 30 | leftDelim string 31 | rightDelim string 32 | } 33 | 34 | // New allocates a new template with the given name. 35 | func New(name string) *Template { 36 | return &Template{ 37 | name: name, 38 | } 39 | } 40 | 41 | // Name returns the name of the template. 42 | func (t *Template) Name() string { 43 | return t.name 44 | } 45 | 46 | // New allocates a new template associated with the given one and with the same 47 | // delimiters. The association, which is transitive, allows one template to 48 | // invoke another with a {{template}} action. 49 | func (t *Template) New(name string) *Template { 50 | t.init() 51 | return &Template{ 52 | name: name, 53 | common: t.common, 54 | leftDelim: t.leftDelim, 55 | rightDelim: t.rightDelim, 56 | } 57 | } 58 | 59 | func (t *Template) init() { 60 | if t.common == nil { 61 | t.common = new(common) 62 | t.tmpl = make(map[string]*Template) 63 | t.parseFuncs = make(FuncMap) 64 | t.execFuncs = make(map[string]reflect.Value) 65 | } 66 | } 67 | 68 | // Clone returns a duplicate of the template, including all associated 69 | // templates. The actual representation is not copied, but the name space of 70 | // associated templates is, so further calls to Parse in the copy will add 71 | // templates to the copy but not to the original. Clone can be used to prepare 72 | // common templates and use them with variant definitions for other templates 73 | // by adding the variants after the clone is made. 74 | func (t *Template) Clone() (*Template, error) { 75 | nt := t.copy(nil) 76 | nt.init() 77 | nt.tmpl[t.name] = nt 78 | for k, v := range t.tmpl { 79 | if k == t.name { // Already installed. 80 | continue 81 | } 82 | // The associated templates share nt's common structure. 83 | tmpl := v.copy(nt.common) 84 | nt.tmpl[k] = tmpl 85 | } 86 | for k, v := range t.parseFuncs { 87 | nt.parseFuncs[k] = v 88 | } 89 | for k, v := range t.execFuncs { 90 | nt.execFuncs[k] = v 91 | } 92 | return nt, nil 93 | } 94 | 95 | // copy returns a shallow copy of t, with common set to the argument. 96 | func (t *Template) copy(c *common) *Template { 97 | nt := New(t.name) 98 | nt.Tree = t.Tree 99 | nt.common = c 100 | nt.leftDelim = t.leftDelim 101 | nt.rightDelim = t.rightDelim 102 | return nt 103 | } 104 | 105 | // AddParseTree creates a new template with the name and parse tree 106 | // and associates it with t. 107 | func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { 108 | if t.common != nil && t.tmpl[name] != nil { 109 | return nil, fmt.Errorf("template: redefinition of template %q", name) 110 | } 111 | nt := t.New(name) 112 | nt.Tree = tree 113 | t.tmpl[name] = nt 114 | return nt, nil 115 | } 116 | 117 | // Templates returns a slice of the templates associated with t, including t 118 | // itself. 119 | func (t *Template) Templates() []*Template { 120 | if t.common == nil { 121 | return nil 122 | } 123 | // Return a slice so we don't expose the map. 124 | m := make([]*Template, 0, len(t.tmpl)) 125 | for _, v := range t.tmpl { 126 | m = append(m, v) 127 | } 128 | return m 129 | } 130 | 131 | // Delims sets the action delimiters to the specified strings, to be used in 132 | // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template 133 | // definitions will inherit the settings. An empty delimiter stands for the 134 | // corresponding default: {{ or }}. 135 | // The return value is the template, so calls can be chained. 136 | func (t *Template) Delims(left, right string) *Template { 137 | t.leftDelim = left 138 | t.rightDelim = right 139 | return t 140 | } 141 | 142 | // Funcs adds the elements of the argument map to the template's function map. 143 | // It panics if a value in the map is not a function with appropriate return 144 | // type. However, it is legal to overwrite elements of the map. The return 145 | // value is the template, so calls can be chained. 146 | func (t *Template) Funcs(funcMap FuncMap) *Template { 147 | t.init() 148 | addValueFuncs(t.execFuncs, funcMap) 149 | addFuncs(t.parseFuncs, funcMap) 150 | return t 151 | } 152 | 153 | // Lookup returns the template with the given name that is associated with t, 154 | // or nil if there is no such template. 155 | func (t *Template) Lookup(name string) *Template { 156 | if t.common == nil { 157 | return nil 158 | } 159 | return t.tmpl[name] 160 | } 161 | 162 | // Parse parses a string into a template. Nested template definitions will be 163 | // associated with the top-level template t. Parse may be called multiple times 164 | // to parse definitions of templates to associate with t. It is an error if a 165 | // resulting template is non-empty (contains content other than template 166 | // definitions) and would replace a non-empty template with the same name. 167 | // (In multiple calls to Parse with the same receiver template, only one call 168 | // can contain text other than space, comments, and template definitions.) 169 | func (t *Template) Parse(text string) (*Template, error) { 170 | t.init() 171 | trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) 172 | if err != nil { 173 | return nil, err 174 | } 175 | // Add the newly parsed trees, including the one for t, into our common structure. 176 | for name, tree := range trees { 177 | // If the name we parsed is the name of this template, overwrite this template. 178 | // The associate method checks it's not a redefinition. 179 | tmpl := t 180 | if name != t.name { 181 | tmpl = t.New(name) 182 | } 183 | // Even if t == tmpl, we need to install it in the common.tmpl map. 184 | if replace, err := t.associate(tmpl, tree); err != nil { 185 | return nil, err 186 | } else if replace { 187 | tmpl.Tree = tree 188 | } 189 | tmpl.leftDelim = t.leftDelim 190 | tmpl.rightDelim = t.rightDelim 191 | } 192 | return t, nil 193 | } 194 | 195 | // associate installs the new template into the group of templates associated 196 | // with t. It is an error to reuse a name except to overwrite an empty 197 | // template. The two are already known to share the common structure. 198 | // The boolean return value reports wither to store this tree as t.Tree. 199 | func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) { 200 | if new.common != t.common { 201 | panic("internal error: associate not common") 202 | } 203 | name := new.name 204 | if old := t.tmpl[name]; old != nil { 205 | oldIsEmpty := parse.IsEmptyTree(old.Root) 206 | newIsEmpty := parse.IsEmptyTree(tree.Root) 207 | if newIsEmpty { 208 | // Whether old is empty or not, new is empty; no reason to replace old. 209 | return false, nil 210 | } 211 | if !oldIsEmpty { 212 | return false, fmt.Errorf("template: redefinition of template %q", name) 213 | } 214 | } 215 | t.tmpl[name] = new 216 | return true, nil 217 | } 218 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/file1.tmpl: -------------------------------------------------------------------------------- 1 | {{define "x"}}TEXT{{end}} 2 | {{define "dotV"}}{{.V}}{{end}} 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/file2.tmpl: -------------------------------------------------------------------------------- 1 | {{define "dot"}}{{.}}{{end}} 2 | {{define "nested"}}{{template "dot" .}}{{end}} 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/tmpl1.tmpl: -------------------------------------------------------------------------------- 1 | template1 2 | {{define "x"}}x{{end}} 3 | {{template "y"}} 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/tmpl2.tmpl: -------------------------------------------------------------------------------- 1 | template2 2 | {{define "y"}}y{{end}} 3 | {{template "x"}} 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/README.md: -------------------------------------------------------------------------------- 1 | # Units - Helpful unit multipliers and functions for Go 2 | 3 | The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package. 4 | 5 | It allows for code like this: 6 | 7 | ```go 8 | n, err := ParseBase2Bytes("1KB") 9 | // n == 1024 10 | n = units.Mebibyte * 512 11 | ``` 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/bytes.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte, 4 | // etc.). 5 | type Base2Bytes int64 6 | 7 | // Base-2 byte units. 8 | const ( 9 | Kibibyte Base2Bytes = 1024 10 | KiB = Kibibyte 11 | Mebibyte = Kibibyte * 1024 12 | MiB = Mebibyte 13 | Gibibyte = Mebibyte * 1024 14 | GiB = Gibibyte 15 | Tebibyte = Gibibyte * 1024 16 | TiB = Tebibyte 17 | Pebibyte = Tebibyte * 1024 18 | PiB = Pebibyte 19 | Exbibyte = Pebibyte * 1024 20 | EiB = Exbibyte 21 | ) 22 | 23 | var ( 24 | bytesUnitMap = MakeUnitMap("iB", "B", 1024) 25 | oldBytesUnitMap = MakeUnitMap("B", "B", 1024) 26 | ) 27 | 28 | // ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB 29 | // and KiB are both 1024. 30 | func ParseBase2Bytes(s string) (Base2Bytes, error) { 31 | n, err := ParseUnit(s, bytesUnitMap) 32 | if err != nil { 33 | n, err = ParseUnit(s, oldBytesUnitMap) 34 | } 35 | return Base2Bytes(n), err 36 | } 37 | 38 | func (b Base2Bytes) String() string { 39 | return ToString(int64(b), 1024, "iB", "B") 40 | } 41 | 42 | var ( 43 | metricBytesUnitMap = MakeUnitMap("B", "B", 1000) 44 | ) 45 | 46 | // MetricBytes are SI byte units (1000 bytes in a kilobyte). 47 | type MetricBytes SI 48 | 49 | // SI base-10 byte units. 50 | const ( 51 | Kilobyte MetricBytes = 1000 52 | KB = Kilobyte 53 | Megabyte = Kilobyte * 1000 54 | MB = Megabyte 55 | Gigabyte = Megabyte * 1000 56 | GB = Gigabyte 57 | Terabyte = Gigabyte * 1000 58 | TB = Terabyte 59 | Petabyte = Terabyte * 1000 60 | PB = Petabyte 61 | Exabyte = Petabyte * 1000 62 | EB = Exabyte 63 | ) 64 | 65 | // ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes. 66 | func ParseMetricBytes(s string) (MetricBytes, error) { 67 | n, err := ParseUnit(s, metricBytesUnitMap) 68 | return MetricBytes(n), err 69 | } 70 | 71 | func (m MetricBytes) String() string { 72 | return ToString(int64(m), 1000, "B", "B") 73 | } 74 | 75 | // ParseStrictBytes supports both iB and B suffixes for base 2 and metric, 76 | // respectively. That is, KiB represents 1024 and KB represents 1000. 77 | func ParseStrictBytes(s string) (int64, error) { 78 | n, err := ParseUnit(s, bytesUnitMap) 79 | if err != nil { 80 | n, err = ParseUnit(s, metricBytesUnitMap) 81 | } 82 | return int64(n), err 83 | } 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/bytes_test.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestBase2BytesString(t *testing.T) { 10 | assert.Equal(t, Base2Bytes(0).String(), "0B") 11 | assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B") 12 | assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B") 13 | } 14 | 15 | func TestParseBase2Bytes(t *testing.T) { 16 | n, err := ParseBase2Bytes("0B") 17 | assert.NoError(t, err) 18 | assert.Equal(t, 0, n) 19 | n, err = ParseBase2Bytes("1KB") 20 | assert.NoError(t, err) 21 | assert.Equal(t, 1024, n) 22 | n, err = ParseBase2Bytes("1MB1KB25B") 23 | assert.NoError(t, err) 24 | assert.Equal(t, 1049625, n) 25 | n, err = ParseBase2Bytes("1.5MB") 26 | assert.NoError(t, err) 27 | assert.Equal(t, 1572864, n) 28 | } 29 | 30 | func TestMetricBytesString(t *testing.T) { 31 | assert.Equal(t, MetricBytes(0).String(), "0B") 32 | assert.Equal(t, MetricBytes(1001).String(), "1KB1B") 33 | assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B") 34 | } 35 | 36 | func TestParseMetricBytes(t *testing.T) { 37 | n, err := ParseMetricBytes("0B") 38 | assert.NoError(t, err) 39 | assert.Equal(t, 0, n) 40 | n, err = ParseMetricBytes("1KB1B") 41 | assert.NoError(t, err) 42 | assert.Equal(t, 1001, n) 43 | n, err = ParseMetricBytes("1MB1KB25B") 44 | assert.NoError(t, err) 45 | assert.Equal(t, 1001025, n) 46 | n, err = ParseMetricBytes("1.5MB") 47 | assert.NoError(t, err) 48 | assert.Equal(t, 1500000, n) 49 | } 50 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/doc.go: -------------------------------------------------------------------------------- 1 | // Package units provides helpful unit multipliers and functions for Go. 2 | // 3 | // The goal of this package is to have functionality similar to the time [1] package. 4 | // 5 | // 6 | // [1] http://golang.org/pkg/time/ 7 | // 8 | // It allows for code like this: 9 | // 10 | // n, err := ParseBase2Bytes("1KB") 11 | // // n == 1024 12 | // n = units.Mebibyte * 512 13 | package units 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/si.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // SI units. 4 | type SI int64 5 | 6 | // SI unit multiples. 7 | const ( 8 | Kilo SI = 1000 9 | Mega = Kilo * 1000 10 | Giga = Mega * 1000 11 | Tera = Giga * 1000 12 | Peta = Tera * 1000 13 | Exa = Peta * 1000 14 | ) 15 | 16 | func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { 17 | return map[string]float64{ 18 | shortSuffix: 1, 19 | "K" + suffix: float64(scale), 20 | "M" + suffix: float64(scale * scale), 21 | "G" + suffix: float64(scale * scale * scale), 22 | "T" + suffix: float64(scale * scale * scale * scale), 23 | "P" + suffix: float64(scale * scale * scale * scale * scale), 24 | "E" + suffix: float64(scale * scale * scale * scale * scale * scale), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/util.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | siUnits = []string{"", "K", "M", "G", "T", "P", "E"} 11 | ) 12 | 13 | func ToString(n int64, scale int64, suffix, baseSuffix string) string { 14 | mn := len(siUnits) 15 | out := make([]string, mn) 16 | for i, m := range siUnits { 17 | if n%scale != 0 || i == 0 && n == 0 { 18 | s := suffix 19 | if i == 0 { 20 | s = baseSuffix 21 | } 22 | out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s) 23 | } 24 | n /= scale 25 | if n == 0 { 26 | break 27 | } 28 | } 29 | return strings.Join(out, "") 30 | } 31 | 32 | // Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123 33 | var errLeadingInt = errors.New("units: bad [0-9]*") // never printed 34 | 35 | // leadingInt consumes the leading [0-9]* from s. 36 | func leadingInt(s string) (x int64, rem string, err error) { 37 | i := 0 38 | for ; i < len(s); i++ { 39 | c := s[i] 40 | if c < '0' || c > '9' { 41 | break 42 | } 43 | if x >= (1<<63-10)/10 { 44 | // overflow 45 | return 0, "", errLeadingInt 46 | } 47 | x = x*10 + int64(c) - '0' 48 | } 49 | return x, s[i:], nil 50 | } 51 | 52 | func ParseUnit(s string, unitMap map[string]float64) (int64, error) { 53 | // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ 54 | orig := s 55 | f := float64(0) 56 | neg := false 57 | 58 | // Consume [-+]? 59 | if s != "" { 60 | c := s[0] 61 | if c == '-' || c == '+' { 62 | neg = c == '-' 63 | s = s[1:] 64 | } 65 | } 66 | // Special case: if all that is left is "0", this is zero. 67 | if s == "0" { 68 | return 0, nil 69 | } 70 | if s == "" { 71 | return 0, errors.New("units: invalid " + orig) 72 | } 73 | for s != "" { 74 | g := float64(0) // this element of the sequence 75 | 76 | var x int64 77 | var err error 78 | 79 | // The next character must be [0-9.] 80 | if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { 81 | return 0, errors.New("units: invalid " + orig) 82 | } 83 | // Consume [0-9]* 84 | pl := len(s) 85 | x, s, err = leadingInt(s) 86 | if err != nil { 87 | return 0, errors.New("units: invalid " + orig) 88 | } 89 | g = float64(x) 90 | pre := pl != len(s) // whether we consumed anything before a period 91 | 92 | // Consume (\.[0-9]*)? 93 | post := false 94 | if s != "" && s[0] == '.' { 95 | s = s[1:] 96 | pl := len(s) 97 | x, s, err = leadingInt(s) 98 | if err != nil { 99 | return 0, errors.New("units: invalid " + orig) 100 | } 101 | scale := 1.0 102 | for n := pl - len(s); n > 0; n-- { 103 | scale *= 10 104 | } 105 | g += float64(x) / scale 106 | post = pl != len(s) 107 | } 108 | if !pre && !post { 109 | // no digits (e.g. ".s" or "-.s") 110 | return 0, errors.New("units: invalid " + orig) 111 | } 112 | 113 | // Consume unit. 114 | i := 0 115 | for ; i < len(s); i++ { 116 | c := s[i] 117 | if c == '.' || ('0' <= c && c <= '9') { 118 | break 119 | } 120 | } 121 | u := s[:i] 122 | s = s[i:] 123 | unit, ok := unitMap[u] 124 | if !ok { 125 | return 0, errors.New("units: unknown unit " + u + " in " + orig) 126 | } 127 | 128 | f += g * unit 129 | } 130 | 131 | if neg { 132 | f = -f 133 | } 134 | if f < float64(-1<<63) || f > float64(1<<63-1) { 135 | return 0, errors.New("units: overflow parsing unit") 136 | } 137 | return int64(f), nil 138 | } 139 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/mitchellh/go-homedir/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mitchell Hashimoto 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/mitchellh/go-homedir/README.md: -------------------------------------------------------------------------------- 1 | # go-homedir 2 | 3 | This is a Go library for detecting the user's home directory without 4 | the use of cgo, so the library can be used in cross-compilation environments. 5 | 6 | Usage is incredibly simple, just call `homedir.Dir()` to get the home directory 7 | for a user, and `homedir.Expand()` to expand the `~` in a path to the home 8 | directory. 9 | 10 | **Why not just use `os/user`?** The built-in `os/user` package requires 11 | cgo on Darwin systems. This means that any Go code that uses that package 12 | cannot cross compile. But 99% of the time the use for `os/user` is just to 13 | retrieve the home directory, which we can do for the current user without 14 | cgo. This library does that, enabling cross-compilation. 15 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/mitchellh/go-homedir/homedir.go: -------------------------------------------------------------------------------- 1 | package homedir 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "runtime" 10 | "strings" 11 | ) 12 | 13 | // Dir returns the home directory for the executing user. 14 | // 15 | // This uses an OS-specific method for discovering the home directory. 16 | // An error is returned if a home directory cannot be detected. 17 | func Dir() (string, error) { 18 | if runtime.GOOS == "windows" { 19 | return dirWindows() 20 | } 21 | 22 | // Unix-like system, so just assume Unix 23 | return dirUnix() 24 | } 25 | 26 | // Expand expands the path to include the home directory if the path 27 | // is prefixed with `~`. If it isn't prefixed with `~`, the path is 28 | // returned as-is. 29 | func Expand(path string) (string, error) { 30 | if len(path) == 0 { 31 | return path, nil 32 | } 33 | 34 | if path[0] != '~' { 35 | return path, nil 36 | } 37 | 38 | if len(path) > 1 && path[1] != '/' && path[1] != '\\' { 39 | return "", errors.New("cannot expand user-specific home dir") 40 | } 41 | 42 | dir, err := Dir() 43 | if err != nil { 44 | return "", err 45 | } 46 | 47 | return filepath.Join(dir, path[1:]), nil 48 | } 49 | 50 | func dirUnix() (string, error) { 51 | // First prefer the HOME environmental variable 52 | if home := os.Getenv("HOME"); home != "" { 53 | return home, nil 54 | } 55 | 56 | // If that fails, try the shell 57 | var stdout bytes.Buffer 58 | cmd := exec.Command("sh", "-c", "eval echo ~$USER") 59 | cmd.Stdout = &stdout 60 | if err := cmd.Run(); err != nil { 61 | return "", err 62 | } 63 | 64 | result := strings.TrimSpace(stdout.String()) 65 | if result == "" { 66 | return "", errors.New("blank output when reading home directory") 67 | } 68 | 69 | return result, nil 70 | } 71 | 72 | func dirWindows() (string, error) { 73 | drive := os.Getenv("HOMEDRIVE") 74 | path := os.Getenv("HOMEPATH") 75 | home := drive + path 76 | if drive == "" || path == "" { 77 | home = os.Getenv("USERPROFILE") 78 | } 79 | if home == "" { 80 | return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") 81 | } 82 | 83 | return home, nil 84 | } 85 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/mitchellh/go-homedir/homedir_test.go: -------------------------------------------------------------------------------- 1 | package homedir 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/user" 7 | "testing" 8 | ) 9 | 10 | func patchEnv(key, value string) func() { 11 | bck := os.Getenv(key) 12 | deferFunc := func() { 13 | os.Setenv(key, bck) 14 | } 15 | 16 | os.Setenv(key, value) 17 | return deferFunc 18 | } 19 | 20 | func TestDir(t *testing.T) { 21 | u, err := user.Current() 22 | if err != nil { 23 | t.Fatalf("err: %s", err) 24 | } 25 | 26 | dir, err := Dir() 27 | if err != nil { 28 | t.Fatalf("err: %s", err) 29 | } 30 | 31 | if u.HomeDir != dir { 32 | t.Fatalf("%#v != %#v", u.HomeDir, dir) 33 | } 34 | } 35 | 36 | func TestExpand(t *testing.T) { 37 | u, err := user.Current() 38 | if err != nil { 39 | t.Fatalf("err: %s", err) 40 | } 41 | 42 | cases := []struct { 43 | Input string 44 | Output string 45 | Err bool 46 | }{ 47 | { 48 | "/foo", 49 | "/foo", 50 | false, 51 | }, 52 | 53 | { 54 | "~/foo", 55 | fmt.Sprintf("%s/foo", u.HomeDir), 56 | false, 57 | }, 58 | 59 | { 60 | "", 61 | "", 62 | false, 63 | }, 64 | 65 | { 66 | "~", 67 | u.HomeDir, 68 | false, 69 | }, 70 | 71 | { 72 | "~foo/foo", 73 | "", 74 | true, 75 | }, 76 | } 77 | 78 | for _, tc := range cases { 79 | actual, err := Expand(tc.Input) 80 | if (err != nil) != tc.Err { 81 | t.Fatalf("Input: %#v\n\nErr: %s", tc.Input, err) 82 | } 83 | 84 | if actual != tc.Output { 85 | t.Fatalf("Input: %#v\n\nOutput: %#v", tc.Input, actual) 86 | } 87 | } 88 | 89 | defer patchEnv("HOME", "/custom/path/")() 90 | expected := "/custom/path/foo/bar" 91 | actual, err := Expand("~/foo/bar") 92 | 93 | if err != nil { 94 | t.Errorf("No error is expected, got: %v", err) 95 | } else if actual != "/custom/path/foo/bar" { 96 | t.Errorf("Expected: %v; actual: %v", expected, actual) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | install: go get -t -v ./... 3 | go: 1.2 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/app_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestCommander(t *testing.T) { 13 | c := New("test", "test") 14 | ping := c.Command("ping", "Ping an IP address.") 15 | pingTTL := ping.Flag("ttl", "TTL for ICMP packets").Short('t').Default("5s").Duration() 16 | 17 | selected, err := c.Parse([]string{"ping"}) 18 | assert.NoError(t, err) 19 | assert.Equal(t, "ping", selected) 20 | assert.Equal(t, 5*time.Second, *pingTTL) 21 | 22 | selected, err = c.Parse([]string{"ping", "--ttl=10s"}) 23 | assert.NoError(t, err) 24 | assert.Equal(t, "ping", selected) 25 | assert.Equal(t, 10*time.Second, *pingTTL) 26 | } 27 | 28 | func TestRequiredFlags(t *testing.T) { 29 | c := New("test", "test") 30 | c.Flag("a", "a").String() 31 | c.Flag("b", "b").Required().String() 32 | 33 | _, err := c.Parse([]string{"--a=foo"}) 34 | assert.Error(t, err) 35 | _, err = c.Parse([]string{"--b=foo"}) 36 | assert.NoError(t, err) 37 | } 38 | 39 | func TestInvalidDefaultFlagValueErrors(t *testing.T) { 40 | c := New("test", "test") 41 | c.Flag("foo", "foo").Default("a").Int() 42 | _, err := c.Parse([]string{}) 43 | assert.Error(t, err) 44 | } 45 | 46 | func TestInvalidDefaultArgValueErrors(t *testing.T) { 47 | c := New("test", "test") 48 | cmd := c.Command("cmd", "cmd") 49 | cmd.Arg("arg", "arg").Default("one").Int() 50 | _, err := c.Parse([]string{"cmd"}) 51 | assert.Error(t, err) 52 | } 53 | 54 | func TestArgsRequiredAfterNonRequiredErrors(t *testing.T) { 55 | c := New("test", "test") 56 | cmd := c.Command("cmd", "") 57 | cmd.Arg("a", "a").String() 58 | cmd.Arg("b", "b").Required().String() 59 | _, err := c.Parse([]string{"cmd"}) 60 | assert.Error(t, err) 61 | } 62 | 63 | func TestArgsMultipleRequiredThenNonRequired(t *testing.T) { 64 | c := New("test", "test").Terminate(nil).Writer(ioutil.Discard) 65 | cmd := c.Command("cmd", "") 66 | cmd.Arg("a", "a").Required().String() 67 | cmd.Arg("b", "b").Required().String() 68 | cmd.Arg("c", "c").String() 69 | cmd.Arg("d", "d").String() 70 | _, err := c.Parse([]string{"cmd", "a", "b"}) 71 | assert.NoError(t, err) 72 | _, err = c.Parse([]string{}) 73 | assert.Error(t, err) 74 | } 75 | 76 | func TestDispatchCallbackIsCalled(t *testing.T) { 77 | dispatched := false 78 | c := New("test", "") 79 | c.Command("cmd", "").Action(func(*ParseContext) error { 80 | dispatched = true 81 | return nil 82 | }) 83 | 84 | _, err := c.Parse([]string{"cmd"}) 85 | assert.NoError(t, err) 86 | assert.True(t, dispatched) 87 | } 88 | 89 | func TestTopLevelArgWorks(t *testing.T) { 90 | c := New("test", "test") 91 | s := c.Arg("arg", "help").String() 92 | _, err := c.Parse([]string{"foo"}) 93 | assert.NoError(t, err) 94 | assert.Equal(t, "foo", *s) 95 | } 96 | 97 | func TestTopLevelArgCantBeUsedWithCommands(t *testing.T) { 98 | c := New("test", "test") 99 | c.Arg("arg", "help").String() 100 | c.Command("cmd", "help") 101 | _, err := c.Parse([]string{}) 102 | assert.Error(t, err) 103 | } 104 | 105 | func TestTooManyArgs(t *testing.T) { 106 | a := New("test", "test") 107 | a.Arg("a", "").String() 108 | _, err := a.Parse([]string{"a", "b"}) 109 | assert.Error(t, err) 110 | } 111 | 112 | func TestTooManyArgsAfterCommand(t *testing.T) { 113 | a := New("test", "test") 114 | a.Command("a", "") 115 | assert.NoError(t, a.init()) 116 | _, err := a.Parse([]string{"a", "b"}) 117 | assert.Error(t, err) 118 | } 119 | 120 | func TestArgsLooksLikeFlagsWithConsumeRemainder(t *testing.T) { 121 | a := New("test", "") 122 | a.Arg("opts", "").Required().Strings() 123 | _, err := a.Parse([]string{"hello", "-world"}) 124 | assert.Error(t, err) 125 | } 126 | 127 | func TestCommandParseDoesNotResetFlagsToDefault(t *testing.T) { 128 | app := New("test", "") 129 | flag := app.Flag("flag", "").Default("default").String() 130 | app.Command("cmd", "") 131 | 132 | _, err := app.Parse([]string{"--flag=123", "cmd"}) 133 | assert.NoError(t, err) 134 | assert.Equal(t, "123", *flag) 135 | } 136 | 137 | func TestCommandParseDoesNotFailRequired(t *testing.T) { 138 | app := New("test", "") 139 | flag := app.Flag("flag", "").Required().String() 140 | app.Command("cmd", "") 141 | 142 | _, err := app.Parse([]string{"cmd", "--flag=123"}) 143 | assert.NoError(t, err) 144 | assert.Equal(t, "123", *flag) 145 | } 146 | 147 | func TestSelectedCommand(t *testing.T) { 148 | app := New("test", "help") 149 | c0 := app.Command("c0", "") 150 | c0.Command("c1", "") 151 | s, err := app.Parse([]string{"c0", "c1"}) 152 | assert.NoError(t, err) 153 | assert.Equal(t, "c0 c1", s) 154 | } 155 | 156 | func TestSubCommandRequired(t *testing.T) { 157 | app := New("test", "help") 158 | c0 := app.Command("c0", "") 159 | c0.Command("c1", "") 160 | _, err := app.Parse([]string{"c0"}) 161 | assert.Error(t, err) 162 | } 163 | 164 | func TestInterspersedFalse(t *testing.T) { 165 | app := New("test", "help").Interspersed(false) 166 | a1 := app.Arg("a1", "").String() 167 | a2 := app.Arg("a2", "").String() 168 | f1 := app.Flag("flag", "").String() 169 | 170 | _, err := app.Parse([]string{"a1", "--flag=flag"}) 171 | assert.NoError(t, err) 172 | assert.Equal(t, "a1", *a1) 173 | assert.Equal(t, "--flag=flag", *a2) 174 | assert.Equal(t, "", *f1) 175 | } 176 | 177 | func TestInterspersedTrue(t *testing.T) { 178 | // test once with the default value and once with explicit true 179 | for i := 0; i < 2; i++ { 180 | app := New("test", "help") 181 | if i != 0 { 182 | t.Log("Setting explicit") 183 | app.Interspersed(true) 184 | } else { 185 | t.Log("Using default") 186 | } 187 | a1 := app.Arg("a1", "").String() 188 | a2 := app.Arg("a2", "").String() 189 | f1 := app.Flag("flag", "").String() 190 | 191 | _, err := app.Parse([]string{"a1", "--flag=flag"}) 192 | assert.NoError(t, err) 193 | assert.Equal(t, "a1", *a1) 194 | assert.Equal(t, "", *a2) 195 | assert.Equal(t, "flag", *f1) 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/args.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import "fmt" 4 | 5 | type argGroup struct { 6 | args []*ArgClause 7 | } 8 | 9 | func newArgGroup() *argGroup { 10 | return &argGroup{} 11 | } 12 | 13 | func (a *argGroup) have() bool { 14 | return len(a.args) > 0 15 | } 16 | 17 | func (a *argGroup) Arg(name, help string) *ArgClause { 18 | arg := newArg(name, help) 19 | a.args = append(a.args, arg) 20 | return arg 21 | } 22 | 23 | func (a *argGroup) init() error { 24 | required := 0 25 | seen := map[string]struct{}{} 26 | previousArgMustBeLast := false 27 | for i, arg := range a.args { 28 | if previousArgMustBeLast { 29 | return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name) 30 | } 31 | if arg.consumesRemainder() { 32 | previousArgMustBeLast = true 33 | } 34 | if _, ok := seen[arg.name]; ok { 35 | return fmt.Errorf("duplicate argument '%s'", arg.name) 36 | } 37 | seen[arg.name] = struct{}{} 38 | if arg.required && required != i { 39 | return fmt.Errorf("required arguments found after non-required") 40 | } 41 | if arg.required { 42 | required++ 43 | } 44 | if err := arg.init(); err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | } 50 | 51 | type ArgClause struct { 52 | parserMixin 53 | name string 54 | help string 55 | defaultValue string 56 | required bool 57 | action Action 58 | preAction Action 59 | } 60 | 61 | func newArg(name, help string) *ArgClause { 62 | a := &ArgClause{ 63 | name: name, 64 | help: help, 65 | } 66 | return a 67 | } 68 | 69 | func (a *ArgClause) consumesRemainder() bool { 70 | if r, ok := a.value.(remainderArg); ok { 71 | return r.IsCumulative() 72 | } 73 | return false 74 | } 75 | 76 | // Required arguments must be input by the user. They can not have a Default() value provided. 77 | func (a *ArgClause) Required() *ArgClause { 78 | a.required = true 79 | return a 80 | } 81 | 82 | // Default value for this argument. It *must* be parseable by the value of the argument. 83 | func (a *ArgClause) Default(value string) *ArgClause { 84 | a.defaultValue = value 85 | return a 86 | } 87 | 88 | func (a *ArgClause) Action(action Action) *ArgClause { 89 | a.action = action 90 | return a 91 | } 92 | 93 | func (a *ArgClause) PreAction(action Action) *ArgClause { 94 | a.preAction = action 95 | return a 96 | } 97 | 98 | func (a *ArgClause) init() error { 99 | if a.required && a.defaultValue != "" { 100 | return fmt.Errorf("required argument '%s' with unusable default value", a.name) 101 | } 102 | if a.value == nil { 103 | return fmt.Errorf("no parser defined for arg '%s'", a.name) 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/args_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestArgRemainder(t *testing.T) { 10 | app := New("test", "") 11 | v := app.Arg("test", "").Strings() 12 | args := []string{"hello", "world"} 13 | _, err := app.Parse(args) 14 | assert.NoError(t, err) 15 | assert.Equal(t, args, *v) 16 | } 17 | 18 | func TestArgRemainderErrorsWhenNotLast(t *testing.T) { 19 | a := newArgGroup() 20 | a.Arg("test", "").Strings() 21 | a.Arg("test2", "").String() 22 | assert.Error(t, a.init()) 23 | } 24 | 25 | func TestArgMultipleRequired(t *testing.T) { 26 | terminated := false 27 | app := New("test", "") 28 | app.Version("0.0.0") 29 | app.Arg("a", "").Required().String() 30 | app.Arg("b", "").Required().String() 31 | app.Terminate(func(int) { terminated = true }) 32 | 33 | _, err := app.Parse([]string{}) 34 | assert.Error(t, err) 35 | _, err = app.Parse([]string{"A"}) 36 | assert.Error(t, err) 37 | _, err = app.Parse([]string{"A", "B"}) 38 | assert.NoError(t, err) 39 | _, err = app.Parse([]string{"--version"}) 40 | assert.True(t, terminated) 41 | } 42 | 43 | func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) { 44 | app := New("test", "") 45 | app.Arg("a", "").Default("invalid").Bool() 46 | _, err := app.Parse([]string{}) 47 | assert.Error(t, err) 48 | } 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/cmd.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type cmdGroup struct { 9 | app *Application 10 | parent *CmdClause 11 | commands map[string]*CmdClause 12 | commandOrder []*CmdClause 13 | } 14 | 15 | func newCmdGroup(app *Application) *cmdGroup { 16 | return &cmdGroup{ 17 | app: app, 18 | commands: make(map[string]*CmdClause), 19 | } 20 | } 21 | 22 | func (c *cmdGroup) flattenedCommands() (out []*CmdClause) { 23 | for _, cmd := range c.commandOrder { 24 | if len(cmd.commands) == 0 { 25 | out = append(out, cmd) 26 | } 27 | out = append(out, cmd.flattenedCommands()...) 28 | } 29 | return 30 | } 31 | 32 | func (c *cmdGroup) addCommand(name, help string) *CmdClause { 33 | cmd := newCommand(c.app, name, help) 34 | c.commands[name] = cmd 35 | c.commandOrder = append(c.commandOrder, cmd) 36 | return cmd 37 | } 38 | 39 | func (c *cmdGroup) init() error { 40 | seen := map[string]bool{} 41 | for _, cmd := range c.commandOrder { 42 | if seen[cmd.name] { 43 | return fmt.Errorf("duplicate command '%s'", cmd.name) 44 | } 45 | seen[cmd.name] = true 46 | if err := cmd.init(); err != nil { 47 | return err 48 | } 49 | } 50 | return nil 51 | } 52 | func (c *cmdGroup) have() bool { 53 | return len(c.commands) > 0 54 | } 55 | 56 | type CmdClauseValidator func(*CmdClause) error 57 | 58 | // A CmdClause is a single top-level command. It encapsulates a set of flags 59 | // and either subcommands or positional arguments. 60 | type CmdClause struct { 61 | *flagGroup 62 | *argGroup 63 | *cmdGroup 64 | app *Application 65 | name string 66 | help string 67 | action Action 68 | preAction Action 69 | validator CmdClauseValidator 70 | hidden bool 71 | } 72 | 73 | func newCommand(app *Application, name, help string) *CmdClause { 74 | c := &CmdClause{ 75 | flagGroup: newFlagGroup(), 76 | argGroup: newArgGroup(), 77 | cmdGroup: newCmdGroup(app), 78 | app: app, 79 | name: name, 80 | help: help, 81 | } 82 | return c 83 | } 84 | 85 | // Validate sets a validation function to run when parsing. 86 | func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause { 87 | c.validator = validator 88 | return c 89 | } 90 | 91 | func (c *CmdClause) FullCommand() string { 92 | out := []string{c.name} 93 | for p := c.parent; p != nil; p = p.parent { 94 | out = append([]string{p.name}, out...) 95 | } 96 | return strings.Join(out, " ") 97 | } 98 | 99 | // Command adds a new sub-command. 100 | func (c *CmdClause) Command(name, help string) *CmdClause { 101 | cmd := c.addCommand(name, help) 102 | cmd.parent = c 103 | return cmd 104 | } 105 | 106 | func (c *CmdClause) Action(action Action) *CmdClause { 107 | c.action = action 108 | return c 109 | } 110 | 111 | func (c *CmdClause) PreAction(action Action) *CmdClause { 112 | c.preAction = action 113 | return c 114 | } 115 | 116 | func (c *CmdClause) init() error { 117 | if err := c.flagGroup.init(); err != nil { 118 | return err 119 | } 120 | if c.argGroup.have() && c.cmdGroup.have() { 121 | return fmt.Errorf("can't mix Arg()s with Command()s") 122 | } 123 | if err := c.argGroup.init(); err != nil { 124 | return err 125 | } 126 | if err := c.cmdGroup.init(); err != nil { 127 | return err 128 | } 129 | return nil 130 | } 131 | 132 | func (c *CmdClause) Hidden() *CmdClause { 133 | c.hidden = true 134 | return c 135 | } 136 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/cmd_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "testing" 9 | ) 10 | 11 | func parseAndExecute(app *Application, context *ParseContext) (string, error) { 12 | if _, err := parse(context, app); err != nil { 13 | return "", err 14 | } 15 | return app.execute(context) 16 | } 17 | 18 | func TestNestedCommands(t *testing.T) { 19 | app := New("app", "") 20 | sub1 := app.Command("sub1", "") 21 | sub1.Flag("sub1", "") 22 | subsub1 := sub1.Command("sub1sub1", "") 23 | subsub1.Command("sub1sub1end", "") 24 | 25 | sub2 := app.Command("sub2", "") 26 | sub2.Flag("sub2", "") 27 | sub2.Command("sub2sub1", "") 28 | 29 | context := tokenize([]string{"sub1", "sub1sub1", "sub1sub1end"}) 30 | selected, err := parseAndExecute(app, context) 31 | assert.NoError(t, err) 32 | assert.True(t, context.EOL()) 33 | assert.Equal(t, "sub1 sub1sub1 sub1sub1end", selected) 34 | } 35 | 36 | func TestNestedCommandsWithArgs(t *testing.T) { 37 | app := New("app", "") 38 | cmd := app.Command("a", "").Command("b", "") 39 | a := cmd.Arg("a", "").String() 40 | b := cmd.Arg("b", "").String() 41 | context := tokenize([]string{"a", "b", "c", "d"}) 42 | selected, err := parseAndExecute(app, context) 43 | assert.NoError(t, err) 44 | assert.True(t, context.EOL()) 45 | assert.Equal(t, "a b", selected) 46 | assert.Equal(t, "c", *a) 47 | assert.Equal(t, "d", *b) 48 | } 49 | 50 | func TestNestedCommandsWithFlags(t *testing.T) { 51 | app := New("app", "") 52 | cmd := app.Command("a", "").Command("b", "") 53 | a := cmd.Flag("aaa", "").Short('a').String() 54 | b := cmd.Flag("bbb", "").Short('b').String() 55 | err := app.init() 56 | assert.NoError(t, err) 57 | context := tokenize(strings.Split("a b --aaa x -b x", " ")) 58 | selected, err := parseAndExecute(app, context) 59 | assert.NoError(t, err) 60 | assert.True(t, context.EOL()) 61 | assert.Equal(t, "a b", selected) 62 | assert.Equal(t, "x", *a) 63 | assert.Equal(t, "x", *b) 64 | } 65 | 66 | func TestNestedCommandWithMergedFlags(t *testing.T) { 67 | app := New("app", "") 68 | cmd0 := app.Command("a", "") 69 | cmd0f0 := cmd0.Flag("aflag", "").Bool() 70 | // cmd1 := app.Command("b", "") 71 | // cmd1f0 := cmd0.Flag("bflag", "").Bool() 72 | cmd00 := cmd0.Command("aa", "") 73 | cmd00f0 := cmd00.Flag("aaflag", "").Bool() 74 | err := app.init() 75 | assert.NoError(t, err) 76 | context := tokenize(strings.Split("a aa --aflag --aaflag", " ")) 77 | selected, err := parseAndExecute(app, context) 78 | assert.NoError(t, err) 79 | assert.True(t, *cmd0f0) 80 | assert.True(t, *cmd00f0) 81 | assert.Equal(t, "a aa", selected) 82 | } 83 | 84 | func TestNestedCommandWithDuplicateFlagErrors(t *testing.T) { 85 | app := New("app", "") 86 | app.Flag("test", "").Bool() 87 | app.Command("cmd0", "").Flag("test", "").Bool() 88 | err := app.init() 89 | assert.Error(t, err) 90 | } 91 | 92 | func TestNestedCommandWithArgAndMergedFlags(t *testing.T) { 93 | app := New("app", "") 94 | cmd0 := app.Command("a", "") 95 | cmd0f0 := cmd0.Flag("aflag", "").Bool() 96 | // cmd1 := app.Command("b", "") 97 | // cmd1f0 := cmd0.Flag("bflag", "").Bool() 98 | cmd00 := cmd0.Command("aa", "") 99 | cmd00a0 := cmd00.Arg("arg", "").String() 100 | cmd00f0 := cmd00.Flag("aaflag", "").Bool() 101 | err := app.init() 102 | assert.NoError(t, err) 103 | context := tokenize(strings.Split("a aa hello --aflag --aaflag", " ")) 104 | selected, err := parseAndExecute(app, context) 105 | assert.NoError(t, err) 106 | assert.True(t, *cmd0f0) 107 | assert.True(t, *cmd00f0) 108 | assert.Equal(t, "a aa", selected) 109 | assert.Equal(t, "hello", *cmd00a0) 110 | } 111 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/doc.go: -------------------------------------------------------------------------------- 1 | // Package kingpin provides command line interfaces like this: 2 | // 3 | // $ chat 4 | // usage: chat [] [] [ ...] 5 | // 6 | // Flags: 7 | // --debug enable debug mode 8 | // --help Show help. 9 | // --server=127.0.0.1 server address 10 | // 11 | // Commands: 12 | // help 13 | // Show help for a command. 14 | // 15 | // post [] 16 | // Post a message to a channel. 17 | // 18 | // register 19 | // Register a new user. 20 | // 21 | // $ chat help post 22 | // usage: chat [] post [] [] 23 | // 24 | // Post a message to a channel. 25 | // 26 | // Flags: 27 | // --image=IMAGE image to post 28 | // 29 | // Args: 30 | // channel to post to 31 | // [] text to post 32 | // $ chat post --image=~/Downloads/owls.jpg pics 33 | // 34 | // From code like this: 35 | // 36 | // package main 37 | // 38 | // import "gopkg.in/alecthomas/kingpin.v1" 39 | // 40 | // var ( 41 | // debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool() 42 | // serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP() 43 | // 44 | // register = kingpin.Command("register", "Register a new user.") 45 | // registerNick = register.Arg("nick", "nickname for user").Required().String() 46 | // registerName = register.Arg("name", "name of user").Required().String() 47 | // 48 | // post = kingpin.Command("post", "Post a message to a channel.") 49 | // postImage = post.Flag("image", "image to post").ExistingFile() 50 | // postChannel = post.Arg("channel", "channel to post to").Required().String() 51 | // postText = post.Arg("text", "text to post").String() 52 | // ) 53 | // 54 | // func main() { 55 | // switch kingpin.Parse() { 56 | // // Register user 57 | // case "register": 58 | // println(*registerNick) 59 | // 60 | // // Post message 61 | // case "post": 62 | // if *postImage != nil { 63 | // } 64 | // if *postText != "" { 65 | // } 66 | // } 67 | // } 68 | package kingpin 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/chat1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/alecthomas/kingpin" 7 | ) 8 | 9 | var ( 10 | debug = kingpin.Flag("debug", "Enable debug mode.").Bool() 11 | timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration() 12 | ip = kingpin.Arg("ip", "IP address to ping.").Required().IP() 13 | count = kingpin.Arg("count", "Number of packets to send").Int() 14 | ) 15 | 16 | func main() { 17 | kingpin.Version("0.0.1") 18 | kingpin.Parse() 19 | fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count) 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/chat2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/alecthomas/kingpin" 8 | ) 9 | 10 | var ( 11 | app = kingpin.New("chat", "A command-line chat application.") 12 | debug = app.Flag("debug", "Enable debug mode.").Bool() 13 | serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP() 14 | 15 | register = app.Command("register", "Register a new user.") 16 | registerNick = register.Arg("nick", "Nickname for user.").Required().String() 17 | registerName = register.Arg("name", "Name of user.").Required().String() 18 | 19 | post = app.Command("post", "Post a message to a channel.") 20 | postImage = post.Flag("image", "Image to post.").File() 21 | postChannel = post.Arg("channel", "Channel to post to.").Required().String() 22 | postText = post.Arg("text", "Text to post.").Strings() 23 | ) 24 | 25 | func main() { 26 | switch kingpin.MustParse(app.Parse(os.Args[1:])) { 27 | // Register user 28 | case register.FullCommand(): 29 | println(*registerNick) 30 | 31 | // Post message 32 | case post.FullCommand(): 33 | if *postImage != nil { 34 | } 35 | text := strings.Join(*postText, " ") 36 | println("Post:", text) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/curl/main.go: -------------------------------------------------------------------------------- 1 | // A curl-like HTTP command-line client. 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "strings" 11 | 12 | "github.com/alecthomas/kingpin" 13 | ) 14 | 15 | var ( 16 | timeout = kingpin.Flag("timeout", "Set connection timeout.").Short('t').Default("5s").Duration() 17 | headers = HTTPHeader(kingpin.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER=VALUE")) 18 | 19 | get = kingpin.Command("get", "GET a resource.") 20 | getFlag = get.Flag("test", "Test flag").Bool() 21 | getURL = get.Command("url", "Retrieve a URL.") 22 | getURLURL = getURL.Arg("url", "URL to GET.").Required().URL() 23 | getFile = get.Command("file", "Retrieve a file.") 24 | getFileFile = getFile.Arg("file", "File to retrieve.").Required().ExistingFile() 25 | 26 | post = kingpin.Command("post", "POST a resource.") 27 | postData = post.Flag("data", "Key-value data to POST").Short('d').PlaceHolder("KEY:VALUE").StringMap() 28 | postBinaryFile = post.Flag("data-binary", "File with binary data to POST.").File() 29 | postURL = post.Arg("url", "URL to POST to.").Required().URL() 30 | ) 31 | 32 | type HTTPHeaderValue http.Header 33 | 34 | func (h HTTPHeaderValue) Set(value string) error { 35 | parts := strings.SplitN(value, "=", 2) 36 | if len(parts) != 2 { 37 | return fmt.Errorf("expected HEADER=VALUE got '%s'", value) 38 | } 39 | (http.Header)(h).Add(parts[0], parts[1]) 40 | return nil 41 | } 42 | 43 | func (h HTTPHeaderValue) String() string { 44 | return "" 45 | } 46 | 47 | func HTTPHeader(s kingpin.Settings) (target *http.Header) { 48 | target = &http.Header{} 49 | s.SetValue((*HTTPHeaderValue)(target)) 50 | return 51 | } 52 | 53 | func applyRequest(req *http.Request) error { 54 | req.Header = *headers 55 | resp, err := http.DefaultClient.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | defer resp.Body.Close() 60 | if resp.StatusCode < 200 || resp.StatusCode > 299 { 61 | return fmt.Errorf("HTTP request failed: %s", resp.Status) 62 | } 63 | _, err = io.Copy(os.Stdout, resp.Body) 64 | return err 65 | } 66 | 67 | func apply(method string, url string) error { 68 | req, err := http.NewRequest(method, url, nil) 69 | if err != nil { 70 | return err 71 | } 72 | return applyRequest(req) 73 | } 74 | 75 | func applyPOST() error { 76 | req, err := http.NewRequest("POST", (*postURL).String(), nil) 77 | if err != nil { 78 | return err 79 | } 80 | if len(*postData) > 0 { 81 | for key, value := range *postData { 82 | req.Form.Set(key, value) 83 | } 84 | } else if postBinaryFile != nil { 85 | if headers.Get("Content-Type") != "" { 86 | headers.Set("Content-Type", "application/octet-stream") 87 | } 88 | req.Body = *postBinaryFile 89 | } else { 90 | return errors.New("--data or --data-binary must be provided to POST") 91 | } 92 | return applyRequest(req) 93 | } 94 | 95 | func main() { 96 | kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version("1.0").Author("Alec Thomas") 97 | kingpin.CommandLine.Help = "An example implementation of curl." 98 | switch kingpin.Parse() { 99 | case "get url": 100 | kingpin.FatalIfError(apply("GET", (*getURLURL).String()), "GET failed") 101 | 102 | case "post": 103 | kingpin.FatalIfError(applyPOST(), "POST failed") 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/modular/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/alecthomas/kingpin" 8 | ) 9 | 10 | // Context for "ls" command 11 | type LsCommand struct { 12 | All bool 13 | } 14 | 15 | func (l *LsCommand) run(c *kingpin.ParseContext) error { 16 | fmt.Printf("all=%v\n", l.All) 17 | return nil 18 | } 19 | 20 | func configureLsCommand(app *kingpin.Application) { 21 | c := &LsCommand{} 22 | ls := app.Command("ls", "List files.").Action(c.run) 23 | ls.Flag("all", "List all files.").Short('a').BoolVar(&c.All) 24 | } 25 | 26 | func main() { 27 | app := kingpin.New("modular", "My modular application.") 28 | configureLsCommand(app) 29 | kingpin.MustParse(app.Parse(os.Args[1:])) 30 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/ping/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/alecthomas/kingpin" 7 | ) 8 | 9 | var ( 10 | debug = kingpin.Flag("debug", "Enable debug mode.").Bool() 11 | timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").OverrideDefaultFromEnvar("PING_TIMEOUT").Required().Short('t').Duration() 12 | ip = kingpin.Arg("ip", "IP address to ping.").Required().IP() 13 | count = kingpin.Arg("count", "Number of packets to send").Int() 14 | ) 15 | 16 | func main() { 17 | kingpin.Version("0.0.1") 18 | kingpin.Parse() 19 | fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count) 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | type HTTPHeaderValue http.Header 10 | 11 | func (h *HTTPHeaderValue) Set(value string) error { 12 | parts := strings.SplitN(value, ":", 2) 13 | if len(parts) != 2 { 14 | return fmt.Errorf("expected HEADER:VALUE got '%s'", value) 15 | } 16 | (*http.Header)(h).Add(parts[0], parts[1]) 17 | return nil 18 | } 19 | 20 | func (h *HTTPHeaderValue) String() string { 21 | return "" 22 | } 23 | 24 | func HTTPHeader(s Settings) (target *http.Header) { 25 | target = new(http.Header) 26 | s.SetValue((*HTTPHeaderValue)(target)) 27 | return 28 | } 29 | 30 | // This example ilustrates how to define custom parsers. HTTPHeader 31 | // cumulatively parses each encountered --header flag into a http.Header struct. 32 | func ExampleValue() { 33 | var ( 34 | curl = New("curl", "transfer a URL") 35 | headers = HTTPHeader(curl.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER:VALUE")) 36 | ) 37 | 38 | curl.Parse([]string{"-H Content-Type:application/octet-stream"}) 39 | for key, value := range *headers { 40 | fmt.Printf("%s = %s\n", key, value) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/flags.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type flagGroup struct { 10 | short map[string]*FlagClause 11 | long map[string]*FlagClause 12 | flagOrder []*FlagClause 13 | } 14 | 15 | func newFlagGroup() *flagGroup { 16 | return &flagGroup{ 17 | short: make(map[string]*FlagClause), 18 | long: make(map[string]*FlagClause), 19 | } 20 | } 21 | 22 | func (f *flagGroup) merge(o *flagGroup) { 23 | for _, flag := range o.flagOrder { 24 | if flag.shorthand != 0 { 25 | f.short[string(flag.shorthand)] = flag 26 | } 27 | f.long[flag.name] = flag 28 | f.flagOrder = append(f.flagOrder, flag) 29 | } 30 | } 31 | 32 | // Flag defines a new flag with the given long name and help. 33 | func (f *flagGroup) Flag(name, help string) *FlagClause { 34 | flag := newFlag(name, help) 35 | f.long[name] = flag 36 | f.flagOrder = append(f.flagOrder, flag) 37 | return flag 38 | } 39 | 40 | func (f *flagGroup) init() error { 41 | for _, flag := range f.long { 42 | if err := flag.init(); err != nil { 43 | return err 44 | } 45 | if flag.shorthand != 0 { 46 | f.short[string(flag.shorthand)] = flag 47 | } 48 | } 49 | return nil 50 | } 51 | 52 | func (f *flagGroup) parse(context *ParseContext) error { 53 | var token *Token 54 | 55 | loop: 56 | for { 57 | token = context.Peek() 58 | switch token.Type { 59 | case TokenEOL: 60 | break loop 61 | 62 | case TokenLong, TokenShort: 63 | flagToken := token 64 | defaultValue := "" 65 | var flag *FlagClause 66 | var ok bool 67 | invert := false 68 | 69 | name := token.Value 70 | if token.Type == TokenLong { 71 | if strings.HasPrefix(name, "no-") { 72 | name = name[3:] 73 | invert = true 74 | } 75 | flag, ok = f.long[name] 76 | if !ok { 77 | return fmt.Errorf("unknown long flag '%s'", flagToken) 78 | } 79 | } else { 80 | flag, ok = f.short[name] 81 | if !ok { 82 | return fmt.Errorf("unknown short flag '%s'", flagToken) 83 | } 84 | } 85 | 86 | context.Next() 87 | 88 | fb, ok := flag.value.(boolFlag) 89 | if ok && fb.IsBoolFlag() { 90 | if invert { 91 | defaultValue = "false" 92 | } else { 93 | defaultValue = "true" 94 | } 95 | } else { 96 | if invert { 97 | return fmt.Errorf("unknown long flag '%s'", flagToken) 98 | } 99 | token = context.Peek() 100 | if token.Type != TokenArg { 101 | return fmt.Errorf("expected argument for flag '%s'", flagToken) 102 | } 103 | context.Next() 104 | defaultValue = token.Value 105 | } 106 | 107 | context.matchedFlag(flag, defaultValue) 108 | 109 | default: 110 | break loop 111 | } 112 | } 113 | return nil 114 | } 115 | 116 | func (f *flagGroup) visibleFlags() int { 117 | count := 0 118 | for _, flag := range f.long { 119 | if !flag.hidden { 120 | count++ 121 | } 122 | } 123 | return count 124 | } 125 | 126 | // FlagClause is a fluid interface used to build flags. 127 | type FlagClause struct { 128 | parserMixin 129 | name string 130 | shorthand byte 131 | help string 132 | envar string 133 | defaultValue string 134 | placeholder string 135 | action Action 136 | preAction Action 137 | hidden bool 138 | } 139 | 140 | func newFlag(name, help string) *FlagClause { 141 | f := &FlagClause{ 142 | name: name, 143 | help: help, 144 | } 145 | return f 146 | } 147 | 148 | func (f *FlagClause) needsValue() bool { 149 | return f.required && f.defaultValue == "" 150 | } 151 | 152 | func (f *FlagClause) formatPlaceHolder() string { 153 | if f.placeholder != "" { 154 | return f.placeholder 155 | } 156 | if f.defaultValue != "" { 157 | if _, ok := f.value.(*stringValue); ok { 158 | return fmt.Sprintf("%q", f.defaultValue) 159 | } 160 | return f.defaultValue 161 | } 162 | return strings.ToUpper(f.name) 163 | } 164 | 165 | func (f *FlagClause) init() error { 166 | if f.required && f.defaultValue != "" { 167 | return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name) 168 | } 169 | if f.value == nil { 170 | return fmt.Errorf("no type defined for --%s (eg. .String())", f.name) 171 | } 172 | if f.envar != "" { 173 | if v := os.Getenv(f.envar); v != "" { 174 | f.defaultValue = v 175 | } 176 | } 177 | return nil 178 | } 179 | 180 | // Dispatch to the given function after the flag is parsed and validated. 181 | func (f *FlagClause) Action(action Action) *FlagClause { 182 | f.action = action 183 | return f 184 | } 185 | 186 | func (f *FlagClause) PreAction(action Action) *FlagClause { 187 | f.preAction = action 188 | return f 189 | } 190 | 191 | // Default value for this flag. It *must* be parseable by the value of the flag. 192 | func (f *FlagClause) Default(value string) *FlagClause { 193 | f.defaultValue = value 194 | return f 195 | } 196 | 197 | // OverrideDefaultFromEnvar overrides the default value for a flag from an 198 | // environment variable, if available. 199 | func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause { 200 | f.envar = envar 201 | return f 202 | } 203 | 204 | // PlaceHolder sets the place-holder string used for flag values in the help. The 205 | // default behaviour is to use the value provided by Default() if provided, 206 | // then fall back on the capitalized flag name. 207 | func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause { 208 | f.placeholder = placeholder 209 | return f 210 | } 211 | 212 | // Hidden hides a flag from usage but still allows it to be used. 213 | func (f *FlagClause) Hidden() *FlagClause { 214 | f.hidden = true 215 | return f 216 | } 217 | 218 | // Required makes the flag required. You can not provide a Default() value to a Required() flag. 219 | func (f *FlagClause) Required() *FlagClause { 220 | f.required = true 221 | return f 222 | } 223 | 224 | // Short sets the short flag name. 225 | func (f *FlagClause) Short(name byte) *FlagClause { 226 | f.shorthand = name 227 | return f 228 | } 229 | 230 | // Bool makes this flag a boolean flag. 231 | func (f *FlagClause) Bool() (target *bool) { 232 | target = new(bool) 233 | f.SetValue(newBoolValue(target)) 234 | return 235 | } 236 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/flags_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "testing" 9 | ) 10 | 11 | func TestBool(t *testing.T) { 12 | app := New("test", "") 13 | b := app.Flag("b", "").Bool() 14 | _, err := app.Parse([]string{"--b"}) 15 | assert.NoError(t, err) 16 | assert.True(t, *b) 17 | } 18 | 19 | func TestNoBool(t *testing.T) { 20 | fg := newFlagGroup() 21 | f := fg.Flag("b", "").Default("true") 22 | b := f.Bool() 23 | fg.init() 24 | tokens := tokenize([]string{"--no-b"}) 25 | err := fg.parse(tokens) 26 | assert.NoError(t, err) 27 | assert.False(t, *b) 28 | } 29 | 30 | func TestNegateNonBool(t *testing.T) { 31 | fg := newFlagGroup() 32 | f := fg.Flag("b", "") 33 | f.Int() 34 | fg.init() 35 | tokens := tokenize([]string{"--no-b"}) 36 | err := fg.parse(tokens) 37 | assert.Error(t, err) 38 | } 39 | 40 | func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) { 41 | app := New("test", "") 42 | app.Flag("a", "").Default("invalid").Bool() 43 | _, err := app.Parse([]string{}) 44 | assert.Error(t, err) 45 | } 46 | 47 | func TestRequiredFlag(t *testing.T) { 48 | app := New("test", "") 49 | app.Version("0.0.0") 50 | app.Terminate(nil) 51 | app.Flag("a", "").Required().Bool() 52 | _, err := app.Parse([]string{"--a"}) 53 | assert.NoError(t, err) 54 | _, err = app.Parse([]string{}) 55 | assert.Error(t, err) 56 | _, err = app.Parse([]string{"--version"}) 57 | } 58 | 59 | func TestShortFlag(t *testing.T) { 60 | app := New("test", "") 61 | f := app.Flag("long", "").Short('s').Bool() 62 | _, err := app.Parse([]string{"-s"}) 63 | assert.NoError(t, err) 64 | assert.True(t, *f) 65 | } 66 | 67 | func TestCombinedShortFlags(t *testing.T) { 68 | app := New("test", "") 69 | a := app.Flag("short0", "").Short('0').Bool() 70 | b := app.Flag("short1", "").Short('1').Bool() 71 | c := app.Flag("short2", "").Short('2').Bool() 72 | _, err := app.Parse([]string{"-01"}) 73 | assert.NoError(t, err) 74 | assert.True(t, *a) 75 | assert.True(t, *b) 76 | assert.False(t, *c) 77 | } 78 | 79 | func TestCombinedShortFlagArg(t *testing.T) { 80 | a := New("test", "") 81 | n := a.Flag("short", "").Short('s').Int() 82 | _, err := a.Parse([]string{"-s10"}) 83 | assert.NoError(t, err) 84 | assert.Equal(t, 10, *n) 85 | } 86 | 87 | func TestEmptyShortFlagIsAnError(t *testing.T) { 88 | _, err := New("test", "").Parse([]string{"-"}) 89 | assert.Error(t, err) 90 | } 91 | 92 | func TestRequiredWithEnvarMissingErrors(t *testing.T) { 93 | app := New("test", "") 94 | app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int() 95 | _, err := app.Parse([]string{}) 96 | assert.Error(t, err) 97 | } 98 | 99 | func TestRequiredWithEnvar(t *testing.T) { 100 | os.Setenv("TEST_ENVAR", "123") 101 | app := New("test", "") 102 | flag := app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int() 103 | _, err := app.Parse([]string{}) 104 | assert.NoError(t, err) 105 | assert.Equal(t, 123, *flag) 106 | } 107 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/genrepeated/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "os/exec" 6 | "strings" 7 | "text/template" 8 | 9 | "os" 10 | ) 11 | 12 | const ( 13 | tmpl = `package kingpin 14 | 15 | // This file is autogenerated by "go generate .". Do not modify. 16 | 17 | {{range .}} 18 | // {{if .Comment}}{{.Comment}}{{else}}{{.|Name}} accumulates {{.Type}} values into a slice.{{end}} 19 | func (p *parserMixin) {{.|Name}}() (target *[]{{.Type}}) { 20 | target = new([]{{.Type}}) 21 | p.{{.|Name}}Var(target) 22 | return 23 | } 24 | 25 | func (p *parserMixin) {{.|Name}}Var(target *[]{{.Type}}) { 26 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return new{{.Name}}Value(v.(*{{.Type}})) })) 27 | } 28 | 29 | {{end}} 30 | ` 31 | ) 32 | 33 | type Repeated struct { 34 | Name string `json:"name"` 35 | Comment string `json:"comment"` 36 | Plural string `json:"plural"` 37 | Type string `json:"type"` 38 | } 39 | 40 | func fatalIfError(err error) { 41 | if err != nil { 42 | panic(err) 43 | } 44 | } 45 | 46 | func main() { 47 | r, err := os.Open("repeated.json") 48 | fatalIfError(err) 49 | defer r.Close() 50 | 51 | v := []Repeated{} 52 | err = json.NewDecoder(r).Decode(&v) 53 | fatalIfError(err) 54 | 55 | t, err := template.New("genrepeated").Funcs(template.FuncMap{ 56 | "Lower": strings.ToLower, 57 | "Name": func(v *Repeated) string { 58 | if v.Plural != "" { 59 | return v.Plural 60 | } 61 | return v.Name + "List" 62 | }, 63 | }).Parse(tmpl) 64 | fatalIfError(err) 65 | 66 | w, err := os.Create("repeated.go") 67 | fatalIfError(err) 68 | defer w.Close() 69 | 70 | err = t.Execute(w, v) 71 | fatalIfError(err) 72 | 73 | err = exec.Command("goimports", "-w", "repeated.go").Run() 74 | fatalIfError(err) 75 | } 76 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/global.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | var ( 9 | // CommandLine is the default Kingpin parser. 10 | CommandLine = New(filepath.Base(os.Args[0]), "") 11 | ) 12 | 13 | // Command adds a new command to the default parser. 14 | func Command(name, help string) *CmdClause { 15 | return CommandLine.Command(name, help) 16 | } 17 | 18 | // Flag adds a new flag to the default parser. 19 | func Flag(name, help string) *FlagClause { 20 | return CommandLine.Flag(name, help) 21 | } 22 | 23 | // Arg adds a new argument to the top-level of the default parser. 24 | func Arg(name, help string) *ArgClause { 25 | return CommandLine.Arg(name, help) 26 | } 27 | 28 | // Parse and return the selected command. Will call the termination handler if 29 | // an error is encountered. 30 | func Parse() string { 31 | selected := MustParse(CommandLine.Parse(os.Args[1:])) 32 | if selected == "" && CommandLine.cmdGroup.have() { 33 | Usage() 34 | CommandLine.terminate(0) 35 | } 36 | return selected 37 | } 38 | 39 | // Errorf prints an error message to stderr. 40 | func Errorf(format string, args ...interface{}) { 41 | CommandLine.Errorf(format, args...) 42 | } 43 | 44 | // Fatalf prints an error message to stderr and exits. 45 | func Fatalf(format string, args ...interface{}) { 46 | CommandLine.Fatalf(format, args...) 47 | } 48 | 49 | // FatalIfError prints an error and exits if err is not nil. The error is printed 50 | // with the given prefix. 51 | func FatalIfError(err error, format string, args ...interface{}) { 52 | CommandLine.FatalIfError(err, format, args...) 53 | } 54 | 55 | // FatalUsage prints an error message followed by usage information, then 56 | // exits with a non-zero status. 57 | func FatalUsage(format string, args ...interface{}) { 58 | CommandLine.FatalUsage(format, args...) 59 | } 60 | 61 | // FatalUsageContext writes a printf formatted error message to stderr, then 62 | // usage information for the given ParseContext, before exiting. 63 | func FatalUsageContext(context *ParseContext, format string, args ...interface{}) { 64 | CommandLine.FatalUsageContext(context, format, args...) 65 | } 66 | 67 | // Usage prints usage to stderr. 68 | func Usage() { 69 | CommandLine.Usage(os.Args[1:]) 70 | } 71 | 72 | // Set global usage template to use (defaults to DefaultUsageTemplate). 73 | func UsageTemplate(template string) *Application { 74 | return CommandLine.UsageTemplate(template) 75 | } 76 | 77 | // MustParse can be used with app.Parse(args) to exit with an error if parsing fails. 78 | func MustParse(command string, err error) string { 79 | if err != nil { 80 | Fatalf("%s, try --help", err) 81 | } 82 | return command 83 | } 84 | 85 | // Version adds a flag for displaying the application version number. 86 | func Version(version string) *Application { 87 | return CommandLine.Version(version) 88 | } 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/guesswidth.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd 2 | 3 | package kingpin 4 | 5 | import "io" 6 | 7 | func guessWidth(w io.Writer) int { 8 | return 80 9 | } 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go: -------------------------------------------------------------------------------- 1 | // +build linux freebsd darwin dragonfly netbsd openbsd 2 | 3 | package kingpin 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "strconv" 9 | "syscall" 10 | "unsafe" 11 | ) 12 | 13 | func guessWidth(w io.Writer) int { 14 | // check if COLUMNS env is set to comply with 15 | // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html 16 | colsStr := os.Getenv("COLUMNS") 17 | if colsStr != "" { 18 | if cols, err := strconv.Atoi(colsStr); err == nil { 19 | return cols 20 | } 21 | } 22 | 23 | if t, ok := w.(*os.File); ok { 24 | fd := t.Fd() 25 | var dimensions [4]uint16 26 | 27 | if _, _, err := syscall.Syscall6( 28 | syscall.SYS_IOCTL, 29 | uintptr(fd), 30 | uintptr(syscall.TIOCGWINSZ), 31 | uintptr(unsafe.Pointer(&dimensions)), 32 | 0, 0, 0, 33 | ); err == 0 { 34 | return int(dimensions[1]) 35 | } 36 | } 37 | return 80 38 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/model.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // Data model for Kingpin command-line structure. 10 | 11 | type FlagGroupModel struct { 12 | Flags []*FlagModel 13 | } 14 | 15 | func (f *FlagGroupModel) FlagSummary() string { 16 | out := []string{} 17 | count := 0 18 | for _, flag := range f.Flags { 19 | if flag.Name != "help" { 20 | count++ 21 | } 22 | if flag.Required { 23 | if flag.IsBoolFlag() { 24 | out = append(out, fmt.Sprintf("--[no-]%s", flag.Name)) 25 | } else { 26 | out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder())) 27 | } 28 | } 29 | } 30 | if count != len(out) { 31 | out = append(out, "[]") 32 | } 33 | return strings.Join(out, " ") 34 | } 35 | 36 | type FlagModel struct { 37 | Name string 38 | Help string 39 | Short rune 40 | Default string 41 | Envar string 42 | PlaceHolder string 43 | Required bool 44 | Hidden bool 45 | Value Value 46 | } 47 | 48 | func (f *FlagModel) String() string { 49 | return f.Value.String() 50 | } 51 | 52 | func (f *FlagModel) IsBoolFlag() bool { 53 | if fl, ok := f.Value.(boolFlag); ok { 54 | return fl.IsBoolFlag() 55 | } 56 | return false 57 | } 58 | 59 | func (f *FlagModel) FormatPlaceHolder() string { 60 | if f.PlaceHolder != "" { 61 | return f.PlaceHolder 62 | } 63 | if f.Default != "" { 64 | if _, ok := f.Value.(*stringValue); ok { 65 | return strconv.Quote(f.Default) 66 | } 67 | return f.Default 68 | } 69 | return strings.ToUpper(f.Name) 70 | } 71 | 72 | type ArgGroupModel struct { 73 | Args []*ArgModel 74 | } 75 | 76 | func (a *ArgGroupModel) ArgSummary() string { 77 | depth := 0 78 | out := []string{} 79 | for _, arg := range a.Args { 80 | h := "<" + arg.Name + ">" 81 | if !arg.Required { 82 | h = "[" + h 83 | depth++ 84 | } 85 | out = append(out, h) 86 | } 87 | out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth) 88 | return strings.Join(out, " ") 89 | } 90 | 91 | type ArgModel struct { 92 | Name string 93 | Help string 94 | Default string 95 | Required bool 96 | Value Value 97 | } 98 | 99 | func (a *ArgModel) String() string { 100 | return a.Value.String() 101 | } 102 | 103 | type CmdGroupModel struct { 104 | Commands []*CmdModel 105 | } 106 | 107 | func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) { 108 | for _, cmd := range c.Commands { 109 | if len(cmd.Commands) == 0 { 110 | out = append(out, cmd) 111 | } 112 | out = append(out, cmd.FlattenedCommands()...) 113 | } 114 | return 115 | } 116 | 117 | type CmdModel struct { 118 | Name string 119 | Help string 120 | FullCommand string 121 | Depth int 122 | Hidden bool 123 | *FlagGroupModel 124 | *ArgGroupModel 125 | *CmdGroupModel 126 | } 127 | 128 | func (c *CmdModel) String() string { 129 | return c.FullCommand 130 | } 131 | 132 | type ApplicationModel struct { 133 | Name string 134 | Help string 135 | Version string 136 | Author string 137 | *ArgGroupModel 138 | *CmdGroupModel 139 | *FlagGroupModel 140 | } 141 | 142 | func (a *Application) Model() *ApplicationModel { 143 | return &ApplicationModel{ 144 | Name: a.Name, 145 | Help: a.Help, 146 | Version: a.version, 147 | Author: a.author, 148 | FlagGroupModel: a.flagGroup.Model(), 149 | ArgGroupModel: a.argGroup.Model(), 150 | CmdGroupModel: a.cmdGroup.Model(), 151 | } 152 | } 153 | 154 | func (a *argGroup) Model() *ArgGroupModel { 155 | m := &ArgGroupModel{} 156 | for _, arg := range a.args { 157 | m.Args = append(m.Args, arg.Model()) 158 | } 159 | return m 160 | } 161 | 162 | func (a *ArgClause) Model() *ArgModel { 163 | return &ArgModel{ 164 | Name: a.name, 165 | Help: a.help, 166 | Default: a.defaultValue, 167 | Required: a.required, 168 | Value: a.value, 169 | } 170 | } 171 | 172 | func (f *flagGroup) Model() *FlagGroupModel { 173 | m := &FlagGroupModel{} 174 | for _, fl := range f.flagOrder { 175 | m.Flags = append(m.Flags, fl.Model()) 176 | } 177 | return m 178 | } 179 | 180 | func (f *FlagClause) Model() *FlagModel { 181 | return &FlagModel{ 182 | Name: f.name, 183 | Help: f.help, 184 | Short: rune(f.shorthand), 185 | Default: f.defaultValue, 186 | Envar: f.envar, 187 | PlaceHolder: f.placeholder, 188 | Required: f.required, 189 | Hidden: f.hidden, 190 | Value: f.value, 191 | } 192 | } 193 | 194 | func (c *cmdGroup) Model() *CmdGroupModel { 195 | m := &CmdGroupModel{} 196 | for _, cm := range c.commandOrder { 197 | m.Commands = append(m.Commands, cm.Model()) 198 | } 199 | return m 200 | } 201 | 202 | func (c *CmdClause) Model() *CmdModel { 203 | depth := 0 204 | for i := c; i != nil; i = i.parent { 205 | depth++ 206 | } 207 | return &CmdModel{ 208 | Name: c.name, 209 | Help: c.help, 210 | Depth: depth, 211 | Hidden: c.hidden, 212 | FullCommand: c.FullCommand(), 213 | FlagGroupModel: c.flagGroup.Model(), 214 | ArgGroupModel: c.argGroup.Model(), 215 | CmdGroupModel: c.cmdGroup.Model(), 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parser_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestParserExpandFromFile(t *testing.T) { 12 | f, err := ioutil.TempFile("", "") 13 | assert.NoError(t, err) 14 | defer os.Remove(f.Name()) 15 | f.WriteString("hello\nworld\n") 16 | f.Close() 17 | 18 | app := New("test", "") 19 | arg0 := app.Arg("arg0", "").String() 20 | arg1 := app.Arg("arg1", "").String() 21 | 22 | _, err = app.Parse([]string{"@" + f.Name()}) 23 | assert.NoError(t, err) 24 | assert.Equal(t, "hello", *arg0) 25 | assert.Equal(t, "world", *arg1) 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parsers.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | //go:generate go run ./genrepeated/main.go 4 | 5 | import ( 6 | "net" 7 | "net/url" 8 | "os" 9 | "time" 10 | 11 | "github.com/alecthomas/units" 12 | ) 13 | 14 | type Settings interface { 15 | SetValue(value Value) 16 | } 17 | 18 | type parserMixin struct { 19 | value Value 20 | required bool 21 | } 22 | 23 | func (p *parserMixin) SetValue(value Value) { 24 | p.value = value 25 | } 26 | 27 | // String sets the parser to a string parser. 28 | func (p *parserMixin) String() (target *string) { 29 | target = new(string) 30 | p.StringVar(target) 31 | return 32 | } 33 | 34 | // StringMap provides key=value parsing into a map. 35 | func (p *parserMixin) StringMap() (target *map[string]string) { 36 | target = &(map[string]string{}) 37 | p.StringMapVar(target) 38 | return 39 | } 40 | 41 | // Bool sets the parser to a boolean parser. Supports --no- to disable the flag. 42 | func (p *parserMixin) Bool() (target *bool) { 43 | target = new(bool) 44 | p.BoolVar(target) 45 | return 46 | } 47 | 48 | // Int sets the parser to an int parser. 49 | func (p *parserMixin) Int() (target *int) { 50 | target = new(int) 51 | p.IntVar(target) 52 | return 53 | } 54 | 55 | // Int64 parses an int64 56 | func (p *parserMixin) Int64() (target *int64) { 57 | target = new(int64) 58 | p.Int64Var(target) 59 | return 60 | } 61 | 62 | // Uint64 parses a uint64 63 | func (p *parserMixin) Uint64() (target *uint64) { 64 | target = new(uint64) 65 | p.Uint64Var(target) 66 | return 67 | } 68 | 69 | // Float sets the parser to a float64 parser. 70 | func (p *parserMixin) Float() (target *float64) { 71 | target = new(float64) 72 | p.FloatVar(target) 73 | return 74 | } 75 | 76 | // Duration sets the parser to a time.Duration parser. 77 | func (p *parserMixin) Duration() (target *time.Duration) { 78 | target = new(time.Duration) 79 | p.DurationVar(target) 80 | return 81 | } 82 | 83 | // Bytes parses numeric byte units. eg. 1.5KB 84 | func (p *parserMixin) Bytes() (target *units.Base2Bytes) { 85 | target = new(units.Base2Bytes) 86 | p.BytesVar(target) 87 | return 88 | } 89 | 90 | // IP sets the parser to a net.IP parser. 91 | func (p *parserMixin) IP() (target *net.IP) { 92 | target = new(net.IP) 93 | p.IPVar(target) 94 | return 95 | } 96 | 97 | // TCP (host:port) address. 98 | func (p *parserMixin) TCP() (target **net.TCPAddr) { 99 | target = new(*net.TCPAddr) 100 | p.TCPVar(target) 101 | return 102 | } 103 | 104 | // TCPVar (host:port) address. 105 | func (p *parserMixin) TCPVar(target **net.TCPAddr) { 106 | p.SetValue(newTCPAddrValue(target)) 107 | } 108 | 109 | // ExistingFile sets the parser to one that requires and returns an existing file. 110 | func (p *parserMixin) ExistingFile() (target *string) { 111 | target = new(string) 112 | p.ExistingFileVar(target) 113 | return 114 | } 115 | 116 | // ExistingDir sets the parser to one that requires and returns an existing directory. 117 | func (p *parserMixin) ExistingDir() (target *string) { 118 | target = new(string) 119 | p.ExistingDirVar(target) 120 | return 121 | } 122 | 123 | // ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory. 124 | func (p *parserMixin) ExistingFileOrDir() (target *string) { 125 | target = new(string) 126 | p.ExistingFileOrDirVar(target) 127 | return 128 | } 129 | 130 | // File returns an os.File against an existing file. 131 | func (p *parserMixin) File() (target **os.File) { 132 | target = new(*os.File) 133 | p.FileVar(target) 134 | return 135 | } 136 | 137 | // File attempts to open a File with os.OpenFile(flag, perm). 138 | func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) { 139 | target = new(*os.File) 140 | p.OpenFileVar(target, flag, perm) 141 | return 142 | } 143 | 144 | // URL provides a valid, parsed url.URL. 145 | func (p *parserMixin) URL() (target **url.URL) { 146 | target = new(*url.URL) 147 | p.URLVar(target) 148 | return 149 | } 150 | 151 | // String sets the parser to a string parser. 152 | func (p *parserMixin) StringVar(target *string) { 153 | p.SetValue(newStringValue(target)) 154 | } 155 | 156 | // StringMap provides key=value parsing into a map. 157 | func (p *parserMixin) StringMapVar(target *map[string]string) { 158 | p.SetValue(newStringMapValue(target)) 159 | } 160 | 161 | // Bool sets the parser to a boolean parser. Supports --no- to disable the flag. 162 | func (p *parserMixin) BoolVar(target *bool) { 163 | p.SetValue(newBoolValue(target)) 164 | } 165 | 166 | // Int sets the parser to an int parser. 167 | func (p *parserMixin) IntVar(target *int) { 168 | p.SetValue(newIntValue(target)) 169 | } 170 | 171 | // Int64 parses an int64 172 | func (p *parserMixin) Int64Var(target *int64) { 173 | p.SetValue(newInt64Value(target)) 174 | } 175 | 176 | // Uint64 parses a uint64 177 | func (p *parserMixin) Uint64Var(target *uint64) { 178 | p.SetValue(newUint64Value(target)) 179 | } 180 | 181 | // Float sets the parser to a float64 parser. 182 | func (p *parserMixin) FloatVar(target *float64) { 183 | p.SetValue(newFloat64Value(target)) 184 | } 185 | 186 | // Duration sets the parser to a time.Duration parser. 187 | func (p *parserMixin) DurationVar(target *time.Duration) { 188 | p.SetValue(newDurationValue(target)) 189 | } 190 | 191 | // BytesVar parses numeric byte units. eg. 1.5KB 192 | func (p *parserMixin) BytesVar(target *units.Base2Bytes) { 193 | p.SetValue(newBytesValue(target)) 194 | } 195 | 196 | // IP sets the parser to a net.IP parser. 197 | func (p *parserMixin) IPVar(target *net.IP) { 198 | p.SetValue(newIPValue(target)) 199 | } 200 | 201 | // ExistingFile sets the parser to one that requires and returns an existing file. 202 | func (p *parserMixin) ExistingFileVar(target *string) { 203 | p.SetValue(newExistingFileValue(target)) 204 | } 205 | 206 | // ExistingDir sets the parser to one that requires and returns an existing directory. 207 | func (p *parserMixin) ExistingDirVar(target *string) { 208 | p.SetValue(newExistingDirValue(target)) 209 | } 210 | 211 | // ExistingDir sets the parser to one that requires and returns an existing directory. 212 | func (p *parserMixin) ExistingFileOrDirVar(target *string) { 213 | p.SetValue(newExistingFileOrDirValue(target)) 214 | } 215 | 216 | // FileVar opens an existing file. 217 | func (p *parserMixin) FileVar(target **os.File) { 218 | p.SetValue(newFileValue(target, os.O_RDONLY, 0)) 219 | } 220 | 221 | // OpenFileVar calls os.OpenFile(flag, perm) 222 | func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) { 223 | p.SetValue(newFileValue(target, flag, perm)) 224 | } 225 | 226 | // URL provides a valid, parsed url.URL. 227 | func (p *parserMixin) URLVar(target **url.URL) { 228 | p.SetValue(newURLValue(target)) 229 | } 230 | 231 | // URLList provides a parsed list of url.URL values. 232 | func (p *parserMixin) URLList() (target *[]*url.URL) { 233 | target = new([]*url.URL) 234 | p.URLListVar(target) 235 | return 236 | } 237 | 238 | // URLListVar provides a parsed list of url.URL values. 239 | func (p *parserMixin) URLListVar(target *[]*url.URL) { 240 | p.SetValue(newURLListValue(target)) 241 | } 242 | 243 | // Enum allows a value from a set of options. 244 | func (p *parserMixin) Enum(options ...string) (target *string) { 245 | target = new(string) 246 | p.EnumVar(&target, options...) 247 | return 248 | } 249 | 250 | // EnumVar allows a value from a set of options. 251 | func (p *parserMixin) EnumVar(target **string, options ...string) { 252 | p.SetValue(newEnumFlag(target, options...)) 253 | } 254 | 255 | // Enums allows a set of values from a set of options. 256 | func (p *parserMixin) Enums(options ...string) (target *[]string) { 257 | target = new([]string) 258 | p.EnumsVar(target, options...) 259 | return 260 | } 261 | 262 | // EnumVar allows a value from a set of options. 263 | func (p *parserMixin) EnumsVar(target *[]string, options ...string) { 264 | p.SetValue(newEnumsFlag(target, options...)) 265 | } 266 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parsers_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | "net" 6 | "net/url" 7 | "os" 8 | 9 | "github.com/stretchr/testify/assert" 10 | 11 | "testing" 12 | ) 13 | 14 | func TestParseStrings(t *testing.T) { 15 | p := parserMixin{} 16 | v := p.Strings() 17 | p.value.Set("a") 18 | p.value.Set("b") 19 | assert.Equal(t, []string{"a", "b"}, *v) 20 | } 21 | 22 | func TestStringsStringer(t *testing.T) { 23 | target := []string{} 24 | v := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) }) 25 | v.Set("hello") 26 | v.Set("world") 27 | assert.Equal(t, "hello,world", v.String()) 28 | } 29 | 30 | func TestParseStringMap(t *testing.T) { 31 | p := parserMixin{} 32 | v := p.StringMap() 33 | p.value.Set("a:b") 34 | p.value.Set("b:c") 35 | assert.Equal(t, map[string]string{"a": "b", "b": "c"}, *v) 36 | } 37 | 38 | func TestParseIP(t *testing.T) { 39 | p := parserMixin{} 40 | v := p.IP() 41 | p.value.Set("10.1.1.2") 42 | ip := net.ParseIP("10.1.1.2") 43 | assert.Equal(t, ip, *v) 44 | } 45 | 46 | func TestParseURL(t *testing.T) { 47 | p := parserMixin{} 48 | v := p.URL() 49 | p.value.Set("http://w3.org") 50 | u, err := url.Parse("http://w3.org") 51 | assert.NoError(t, err) 52 | assert.Equal(t, *u, **v) 53 | } 54 | 55 | func TestParseExistingFile(t *testing.T) { 56 | f, err := ioutil.TempFile("", "") 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | defer f.Close() 61 | defer os.Remove(f.Name()) 62 | 63 | p := parserMixin{} 64 | v := p.ExistingFile() 65 | err = p.value.Set(f.Name()) 66 | assert.NoError(t, err) 67 | assert.Equal(t, f.Name(), *v) 68 | err = p.value.Set("/etc/hostsDEFINITELYMISSING") 69 | assert.Error(t, err) 70 | } 71 | 72 | func TestParseTCPAddr(t *testing.T) { 73 | p := parserMixin{} 74 | v := p.TCP() 75 | err := p.value.Set("127.0.0.1:1234") 76 | assert.NoError(t, err) 77 | expected, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234") 78 | assert.NoError(t, err) 79 | assert.Equal(t, *expected, **v) 80 | } 81 | 82 | func TestParseTCPAddrList(t *testing.T) { 83 | p := parserMixin{} 84 | _ = p.TCPList() 85 | err := p.value.Set("127.0.0.1:1234") 86 | assert.NoError(t, err) 87 | err = p.value.Set("127.0.0.1:1235") 88 | assert.NoError(t, err) 89 | assert.Equal(t, "127.0.0.1:1234,127.0.0.1:1235", p.value.String()) 90 | } 91 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/repeated.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | // This file is autogenerated by "go generate .". Do not modify. 9 | 10 | // Strings accumulates string values into a slice. 11 | func (p *parserMixin) Strings() (target *[]string) { 12 | target = new([]string) 13 | p.StringsVar(target) 14 | return 15 | } 16 | 17 | func (p *parserMixin) StringsVar(target *[]string) { 18 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newStringValue(v.(*string)) })) 19 | } 20 | 21 | // Uint64List accumulates uint64 values into a slice. 22 | func (p *parserMixin) Uint64List() (target *[]uint64) { 23 | target = new([]uint64) 24 | p.Uint64ListVar(target) 25 | return 26 | } 27 | 28 | func (p *parserMixin) Uint64ListVar(target *[]uint64) { 29 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint64Value(v.(*uint64)) })) 30 | } 31 | 32 | // Int64List accumulates int64 values into a slice. 33 | func (p *parserMixin) Int64List() (target *[]int64) { 34 | target = new([]int64) 35 | p.Int64ListVar(target) 36 | return 37 | } 38 | 39 | func (p *parserMixin) Int64ListVar(target *[]int64) { 40 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt64Value(v.(*int64)) })) 41 | } 42 | 43 | // DurationList accumulates time.Duration values into a slice. 44 | func (p *parserMixin) DurationList() (target *[]time.Duration) { 45 | target = new([]time.Duration) 46 | p.DurationListVar(target) 47 | return 48 | } 49 | 50 | func (p *parserMixin) DurationListVar(target *[]time.Duration) { 51 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newDurationValue(v.(*time.Duration)) })) 52 | } 53 | 54 | // IPList accumulates net.IP values into a slice. 55 | func (p *parserMixin) IPList() (target *[]net.IP) { 56 | target = new([]net.IP) 57 | p.IPListVar(target) 58 | return 59 | } 60 | 61 | func (p *parserMixin) IPListVar(target *[]net.IP) { 62 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newIPValue(v.(*net.IP)) })) 63 | } 64 | 65 | // TCPList accumulates *net.TCPAddr values into a slice. 66 | func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) { 67 | target = new([]*net.TCPAddr) 68 | p.TCPListVar(target) 69 | return 70 | } 71 | 72 | func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) { 73 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newTCPAddrValue(v.(**net.TCPAddr)) })) 74 | } 75 | 76 | // ExistingFiles accumulates string values into a slice. 77 | func (p *parserMixin) ExistingFiles() (target *[]string) { 78 | target = new([]string) 79 | p.ExistingFilesVar(target) 80 | return 81 | } 82 | 83 | func (p *parserMixin) ExistingFilesVar(target *[]string) { 84 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileValue(v.(*string)) })) 85 | } 86 | 87 | // ExistingDirs accumulates string values into a slice. 88 | func (p *parserMixin) ExistingDirs() (target *[]string) { 89 | target = new([]string) 90 | p.ExistingDirsVar(target) 91 | return 92 | } 93 | 94 | func (p *parserMixin) ExistingDirsVar(target *[]string) { 95 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingDirValue(v.(*string)) })) 96 | } 97 | 98 | // ExistingFilesOrDirs accumulates string values into a slice. 99 | func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) { 100 | target = new([]string) 101 | p.ExistingFilesOrDirsVar(target) 102 | return 103 | } 104 | 105 | func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) { 106 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileOrDirValue(v.(*string)) })) 107 | } 108 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/repeated.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"name": "String", "type": "string", "plural": "Strings"}, 3 | {"name": "Uint64", "type": "uint64"}, 4 | {"name": "Int64", "type": "int64"}, 5 | {"name": "Duration", "type": "time.Duration"}, 6 | {"name": "IP", "type": "net.IP"}, 7 | {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList"}, 8 | {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles"}, 9 | {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs"}, 10 | {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs"} 11 | ] 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/templates.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // Default usage template. 4 | var DefaultUsageTemplate = `{{define "FormatCommand"}}\ 5 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 6 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 7 | {{end}}\ 8 | 9 | {{define "FormatCommands"}}\ 10 | {{range .FlattenedCommands}}\ 11 | {{if not .Hidden}}\ 12 | {{.FullCommand}}{{template "FormatCommand" .}} 13 | {{.Help|Wrap 4}} 14 | {{end}}\ 15 | {{end}}\ 16 | {{end}}\ 17 | 18 | {{define "FormatUsage"}}\ 19 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 20 | {{if .Help}} 21 | {{.Help|Wrap 0}}\ 22 | {{end}}\ 23 | 24 | {{end}}\ 25 | 26 | {{if .Context.SelectedCommand}}\ 27 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 28 | {{else}}\ 29 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 30 | {{end}}\ 31 | {{if .Context.Flags}}\ 32 | Flags: 33 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 34 | {{end}}\ 35 | {{if .Context.Args}}\ 36 | Args: 37 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 38 | {{end}}\ 39 | {{if .Context.SelectedCommand}}\ 40 | Subcommands: 41 | {{if .Context.SelectedCommand.Commands}}\ 42 | {{template "FormatCommands" .Context.SelectedCommand}} 43 | {{end}}\ 44 | {{else if .App.Commands}}\ 45 | Commands: 46 | {{template "FormatCommands" .App}} 47 | {{end}}\ 48 | ` 49 | 50 | // Usage template with compactly formatted commands. 51 | var CompactUsageTemplate = `{{define "FormatCommand"}}\ 52 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 53 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 54 | {{end}}\ 55 | 56 | {{define "FormatCommandList"}}\ 57 | {{range .}}\ 58 | {{if not .Hidden}}\ 59 | {{.Depth|Indent}}{{.Name}}{{template "FormatCommand" .}} 60 | {{end}}\ 61 | {{template "FormatCommandList" .Commands}}\ 62 | {{end}}\ 63 | {{end}}\ 64 | 65 | {{define "FormatUsage"}}\ 66 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 67 | {{if .Help}} 68 | {{.Help|Wrap 0}}\ 69 | {{end}}\ 70 | 71 | {{end}}\ 72 | 73 | {{if .Context.SelectedCommand}}\ 74 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 75 | {{else}}\ 76 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 77 | {{end}}\ 78 | {{if .Context.Flags}}\ 79 | Flags: 80 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 81 | {{end}}\ 82 | {{if .Context.Args}}\ 83 | Args: 84 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 85 | {{end}}\ 86 | {{if .Context.SelectedCommand}}\ 87 | {{if .Context.SelectedCommand.Commands}}\ 88 | Commands: 89 | {{.Context.SelectedCommand}} 90 | {{template "FormatCommandList" .Context.SelectedCommand.Commands}} 91 | {{end}}\ 92 | {{else if .App.Commands}}\ 93 | Commands: 94 | {{template "FormatCommandList" .App.Commands}} 95 | {{end}}\ 96 | ` 97 | 98 | var ManPageTemplate = `{{define "FormatFlags"}}\ 99 | {{range .Flags}}\ 100 | {{if not .Hidden}}\ 101 | .TP 102 | \fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR 103 | {{.Help}} 104 | {{end}}\ 105 | {{end}}\ 106 | {{end}}\ 107 | 108 | {{define "FormatCommand"}}\ 109 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 110 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 111 | {{end}}\ 112 | 113 | {{define "FormatCommands"}}\ 114 | {{range .FlattenedCommands}}\ 115 | {{if not .Hidden}}\ 116 | .SS 117 | \fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR 118 | .PP 119 | {{.Help}} 120 | {{template "FormatFlags" .}}\ 121 | {{end}}\ 122 | {{end}}\ 123 | {{end}}\ 124 | 125 | {{define "FormatUsage"}}\ 126 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}}\\fR 127 | {{end}}\ 128 | 129 | .TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}" 130 | .SH "NAME" 131 | {{.App.Name}} 132 | .SH "SYNOPSIS" 133 | .TP 134 | \fB{{.App.Name}}{{template "FormatUsage" .App}} 135 | .SH "DESCRIPTION" 136 | {{.App.Help}} 137 | .SH "OPTIONS" 138 | {{template "FormatFlags" .App}}\ 139 | {{if .App.Commands}}\ 140 | .SH "COMMANDS" 141 | {{template "FormatCommands" .App}}\ 142 | {{end}}\ 143 | ` 144 | 145 | // Default usage template. 146 | var LongHelpTemplate = `{{define "FormatCommand"}}\ 147 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 148 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 149 | {{end}}\ 150 | 151 | {{define "FormatCommands"}}\ 152 | {{range .FlattenedCommands}}\ 153 | {{if not .Hidden}}\ 154 | {{.FullCommand}}{{template "FormatCommand" .}} 155 | {{.Help|Wrap 4}} 156 | {{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}} 157 | {{end}}\ 158 | {{end}}\ 159 | {{end}}\ 160 | 161 | {{define "FormatUsage"}}\ 162 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 163 | {{if .Help}} 164 | {{.Help|Wrap 0}}\ 165 | {{end}}\ 166 | 167 | {{end}}\ 168 | 169 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 170 | {{if .Context.Flags}}\ 171 | Flags: 172 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 173 | {{end}}\ 174 | {{if .Context.Args}}\ 175 | Args: 176 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 177 | {{end}}\ 178 | {{if .App.Commands}}\ 179 | Commands: 180 | {{template "FormatCommands" .App}} 181 | {{end}}\ 182 | ` 183 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/usage.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/doc" 7 | "io" 8 | "strings" 9 | 10 | "github.com/alecthomas/template" 11 | ) 12 | 13 | var ( 14 | preIndent = " " 15 | ) 16 | 17 | func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) { 18 | // Find size of first column. 19 | s := 0 20 | for _, row := range rows { 21 | if c := len(row[0]); c > s && c < 20 { 22 | s = c 23 | } 24 | } 25 | 26 | indentStr := strings.Repeat(" ", indent) 27 | offsetStr := strings.Repeat(" ", s+padding) 28 | 29 | for _, row := range rows { 30 | buf := bytes.NewBuffer(nil) 31 | doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent) 32 | lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") 33 | fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "") 34 | if len(row[0]) >= 20 { 35 | fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr) 36 | } 37 | fmt.Fprintf(w, "%s\n", lines[0]) 38 | for _, line := range lines[1:] { 39 | fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line) 40 | } 41 | } 42 | } 43 | 44 | // Usage writes application usage to w. It parses args to determine 45 | // appropriate help context, such as which command to show help for. 46 | func (a *Application) Usage(args []string) { 47 | context, err := a.ParseContext(args) 48 | a.FatalIfError(err, "") 49 | if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil { 50 | panic(err) 51 | } 52 | } 53 | 54 | func formatAppUsage(app *ApplicationModel) string { 55 | s := []string{app.Name} 56 | if len(app.Flags) > 0 { 57 | s = append(s, app.FlagSummary()) 58 | } 59 | if len(app.Args) > 0 { 60 | s = append(s, app.ArgSummary()) 61 | } 62 | return strings.Join(s, " ") 63 | } 64 | 65 | func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string { 66 | s := []string{app.Name, cmd.String()} 67 | if len(app.Flags) > 0 { 68 | s = append(s, app.FlagSummary()) 69 | } 70 | if len(app.Args) > 0 { 71 | s = append(s, app.ArgSummary()) 72 | } 73 | return strings.Join(s, " ") 74 | } 75 | 76 | func formatFlag(flag *FlagModel) string { 77 | flagString := "" 78 | if flag.Short != 0 { 79 | flagString += fmt.Sprintf("-%c, ", flag.Short) 80 | } 81 | flagString += fmt.Sprintf("--%s", flag.Name) 82 | if !flag.IsBoolFlag() { 83 | flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder()) 84 | } 85 | return flagString 86 | } 87 | 88 | type templateParseContext struct { 89 | SelectedCommand *CmdModel 90 | *FlagGroupModel 91 | *ArgGroupModel 92 | } 93 | 94 | type templateContext struct { 95 | App *ApplicationModel 96 | Width int 97 | Context *templateParseContext 98 | } 99 | 100 | // UsageForContext displays usage information from a ParseContext (obtained from 101 | // Application.ParseContext() or Action(f) callbacks). 102 | func (a *Application) UsageForContext(context *ParseContext) error { 103 | return a.UsageForContextWithTemplate(context, 2, a.usageTemplate) 104 | } 105 | 106 | // UsageForContextWithTemplate is the base usage function. You generally don't need to use this. 107 | func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error { 108 | width := guessWidth(a.writer) 109 | funcs := template.FuncMap{ 110 | "Indent": func(level int) string { 111 | return strings.Repeat(" ", level*indent) 112 | }, 113 | "Wrap": func(indent int, s string) string { 114 | buf := bytes.NewBuffer(nil) 115 | indentText := strings.Repeat(" ", indent) 116 | doc.ToText(buf, s, indentText, indentText, width) 117 | return buf.String() 118 | }, 119 | "FormatFlag": formatFlag, 120 | "FlagsToTwoColumns": func(f []*FlagModel) [][2]string { 121 | rows := [][2]string{} 122 | for _, flag := range f { 123 | if !flag.Hidden { 124 | rows = append(rows, [2]string{formatFlag(flag), flag.Help}) 125 | } 126 | } 127 | return rows 128 | }, 129 | "ArgsToTwoColumns": func(a []*ArgModel) [][2]string { 130 | rows := [][2]string{} 131 | for _, arg := range a { 132 | s := "<" + arg.Name + ">" 133 | if !arg.Required { 134 | s = "[" + s + "]" 135 | } 136 | rows = append(rows, [2]string{s, arg.Help}) 137 | } 138 | return rows 139 | }, 140 | "FormatTwoColumns": func(rows [][2]string) string { 141 | buf := bytes.NewBuffer(nil) 142 | formatTwoColumns(buf, indent, indent, width, rows) 143 | return buf.String() 144 | }, 145 | "FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string { 146 | buf := bytes.NewBuffer(nil) 147 | formatTwoColumns(buf, indent, padding, width, rows) 148 | return buf.String() 149 | }, 150 | "FormatAppUsage": formatAppUsage, 151 | "FormatCommandUsage": formatCmdUsage, 152 | "IsCumulative": func(value Value) bool { 153 | _, ok := value.(remainderArg) 154 | return ok 155 | }, 156 | "Char": func(c rune) string { 157 | return string(c) 158 | }, 159 | } 160 | t, err := template.New("usage").Funcs(funcs).Parse(tmpl) 161 | if err != nil { 162 | return err 163 | } 164 | var selectedCommand *CmdModel 165 | if context.SelectedCommand != nil { 166 | selectedCommand = context.SelectedCommand.Model() 167 | } 168 | ctx := templateContext{ 169 | App: a.Model(), 170 | Width: width, 171 | Context: &templateParseContext{ 172 | SelectedCommand: selectedCommand, 173 | FlagGroupModel: context.flags.Model(), 174 | ArgGroupModel: context.arguments.Model(), 175 | }, 176 | } 177 | return t.Execute(a.writer, ctx) 178 | } 179 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/usage_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestFormatTwoColumns(t *testing.T) { 13 | buf := bytes.NewBuffer(nil) 14 | formatTwoColumns(buf, 2, 2, 20, [][2]string{ 15 | {"--hello", "Hello world help with something that is cool."}, 16 | }) 17 | expected := ` --hello Hello 18 | world 19 | help with 20 | something 21 | that is 22 | cool. 23 | ` 24 | assert.Equal(t, expected, buf.String()) 25 | } 26 | 27 | func TestFormatTwoColumnsWide(t *testing.T) { 28 | samples := [][2]string{ 29 | {strings.Repeat("x", 19), "19 chars"}, 30 | {strings.Repeat("x", 20), "20 chars"}} 31 | buf := bytes.NewBuffer(nil) 32 | formatTwoColumns(buf, 0, 0, 200, samples) 33 | fmt.Println(buf.String()) 34 | expected := `xxxxxxxxxxxxxxxxxxx19 chars 35 | xxxxxxxxxxxxxxxxxxxx 36 | 20 chars 37 | ` 38 | assert.Equal(t, expected, buf.String()) 39 | } 40 | 41 | func TestHiddenCommand(t *testing.T) { 42 | templates := []struct{ name, template string }{ 43 | {"default", DefaultUsageTemplate}, 44 | {"Compact", CompactUsageTemplate}, 45 | {"Long", LongHelpTemplate}, 46 | {"Man", ManPageTemplate}, 47 | } 48 | 49 | var buf bytes.Buffer 50 | t.Log("1") 51 | 52 | a := New("test", "Test").Writer(&buf).Terminate(nil) 53 | a.Command("visible", "visible") 54 | a.Command("hidden", "hidden").Hidden() 55 | 56 | for _, tp := range templates { 57 | buf.Reset() 58 | a.UsageTemplate(tp.template) 59 | a.Parse(nil) 60 | // a.Parse([]string{"--help"}) 61 | usage := buf.String() 62 | t.Logf("Usage for %s is:\n%s\n", tp.name, usage) 63 | 64 | assert.NotContains(t, usage, "hidden") 65 | assert.Contains(t, usage, "visible") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 New Relic, Inc. 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Archived header](https://github.com/newrelic/open-source-office/raw/master/examples/categories/images/Archived.png)](https://github.com/newrelic/open-source-office/blob/master/examples/categories/index.md#archived) 2 | 3 | # ⛔️ Deprecated ⛔️ 4 | 5 | This project is no longer supported; please consider using https://github.com/howtowhale/dvm instead. 6 | 7 | DKENV 8 | ----- 9 | dkenv is a tool that downloads Docker versions for you, keeps track of your versions, and lets you switch between docker versions on the fly. You can also specify the apiversion on the command line and dkenv will select the correct docker version. 10 | 11 | Designed to bypass the dreaded: 12 | 13 | 2014/08/26 14:21:03 Error response from daemon: client and server don't have same version (client : 1.13, server: 1.12) 14 | 15 | ### Usage 16 | 17 | ``` 18 | $ dkenv client 1.6.0 19 | OR 20 | $ dkenv api 1.18 21 | ``` 22 | 23 | dkenv stores the docker files in ~/.dkenv and creates a symlink in /usr/local/bin 24 | 25 | ### Full list of options 26 | 27 | ``` 28 | usage: dkenv [] [ ...] 29 | 30 | Flags: 31 | --help Show help (also see --help-long and --help-man). 32 | --bindir="/usr/local/bin" 33 | Directory to create symlinks for Docker binaries 34 | --homedir=HOMEDIR Override automatically found homedir 35 | --dkenvdir="~/.dkenv" 36 | Directory to store Docker binaries 37 | -d, --debug Enable debug output 38 | --version Show application version. 39 | 40 | Commands: 41 | help [...] 42 | Show help. 43 | 44 | client 45 | Download/switch Docker binary by *client* version 46 | 47 | api 48 | Download/switch Docker binary by *API* version 49 | 50 | list 51 | List downloaded/existing Docker binaries 52 | ``` 53 | 54 | ### Building 55 | 56 | If you don't have `godep` installed: 57 | ``` 58 | $ go get github.com/tools/godep 59 | ``` 60 | Then build: 61 | ``` 62 | $ $GOPATH/bin/godep go build -o dkenv main.go 63 | ``` 64 | The resulting binary will be in the current working directory. 65 | Or install into `$GOPATH/bin` with: 66 | ``` 67 | $GOPATH/bin/godep go install 68 | ``` 69 | 70 | ### Contributions 71 | 72 | Contributions are more than welcome. Bug reports with specific reproduction 73 | steps are great. If you have a code contribution you'd like to make, open a 74 | pull request with suggested code. 75 | 76 | Pull requests should: 77 | 78 | * Clearly state their intent in the title 79 | * Have a description that explains the need for the changes 80 | * Include tests! 81 | * Not break the public API 82 | * Add yourself to the CONTRIBUTORS file. I might forget. 83 | 84 | If you are simply looking to contribute to the project, taking on one of the 85 | items in the "Future Additions" section above would be a great place to start. 86 | Ping us to let us know you're working on it by opening a GitHub Issue on the 87 | project. 88 | 89 | By contributing to this project you agree that you are granting New Relic a 90 | non-exclusive, non-revokable, no-cost license to use the code, algorithms, 91 | patents, and ideas in that code in our products if we so choose. You also agree 92 | the code is provided as-is and you provide no warranties as to its fitness or 93 | correctness for any purpose 94 | 95 | Copyright (c) 2015 New Relic, Inc. All rights reserved. 96 | -------------------------------------------------------------------------------- /cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/mitchellh/go-homedir" 10 | ) 11 | 12 | type Cli struct { 13 | BinDir *string 14 | HomeDir *string 15 | DkenvDir *string 16 | } 17 | 18 | func New(binDir, homeDir, dkenvDir *string) *Cli { 19 | c := &Cli{ 20 | BinDir: binDir, 21 | HomeDir: homeDir, 22 | DkenvDir: dkenvDir, 23 | } 24 | 25 | return c 26 | } 27 | 28 | // Wrapper for each specific arg handler 29 | func (c *Cli) HandleArgs() error { 30 | if err := c.setHomeDir(); err != nil { 31 | return err 32 | } 33 | 34 | if err := c.handleBinDir(); err != nil { 35 | return err 36 | } 37 | 38 | if err := c.handleDkenvDir(); err != nil { 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | 45 | // If homedir is not set, set it; if it doesn't exist, bail out 46 | func (c *Cli) setHomeDir() error { 47 | if *c.HomeDir == "" { 48 | homeDir, err := homedir.Dir() 49 | if err != nil { 50 | return fmt.Errorf("Unable to fetch current home dir: %v", err) 51 | } 52 | 53 | *c.HomeDir = homeDir 54 | } 55 | 56 | // Ensure it exists 57 | isDir, err := IsDirAndExists(*c.HomeDir) 58 | if err != nil { 59 | return fmt.Errorf("Home directory error: %v", err) 60 | } 61 | 62 | if !isDir { 63 | return errors.New("Home directory does not exist!") 64 | } 65 | 66 | // Strip trailing slash 67 | *c.HomeDir = strings.TrimRight(*c.HomeDir, "/") 68 | 69 | return nil 70 | } 71 | 72 | func (c *Cli) handleBinDir() error { 73 | // Ensure it exists 74 | isDir, err := IsDirAndExists(*c.BinDir) 75 | if err != nil { 76 | return fmt.Errorf("Bin directory error: %v", err) 77 | } 78 | 79 | if !isDir { 80 | return errors.New("Bin directory does not exist!") 81 | } 82 | 83 | // Strip trailing slash 84 | *c.BinDir = strings.TrimRight(*c.BinDir, "/") 85 | 86 | return nil 87 | } 88 | 89 | // Check if dkenv dir exists; if not, create it 90 | func (c *Cli) handleDkenvDir() error { 91 | // Expand the dir 92 | if *c.DkenvDir == "~/.dkenv" { 93 | *c.DkenvDir = *c.HomeDir + "/.dkenv" 94 | } 95 | 96 | isDir, err := IsDirAndExists(*c.DkenvDir) 97 | if err != nil { 98 | return fmt.Errorf("Dkenv directory error: %v", err) 99 | } 100 | 101 | if !isDir { 102 | // Create the dir 103 | if err := os.Mkdir(*c.DkenvDir, 0700); err != nil { 104 | return fmt.Errorf("Error creating dkenv dir (%v): %v", *c.DkenvDir, err) 105 | } 106 | } 107 | 108 | // Strip trailing slash 109 | *c.DkenvDir = strings.TrimRight(*c.DkenvDir, "/") 110 | 111 | return nil 112 | } 113 | 114 | func IsDirAndExists(dir string) (bool, error) { 115 | fi, err := os.Stat(dir) 116 | if err != nil && os.IsNotExist(err) { 117 | return false, nil 118 | } 119 | 120 | // Regular error 121 | if err != nil { 122 | return false, err 123 | } 124 | 125 | if !fi.IsDir() { 126 | return false, nil 127 | } 128 | 129 | return true, nil 130 | } 131 | -------------------------------------------------------------------------------- /cli/cli_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "math/rand" 7 | "os" 8 | "testing" 9 | 10 | "github.com/mitchellh/go-homedir" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | var ( 15 | testBinDir = "/usr/bin" 16 | testHomeDir = "/tmp/test_home_dir" 17 | testDkenvDir = "" // Get's set via init() 18 | ) 19 | 20 | func init() { 21 | // setup dummy dirs 22 | tmpDir, err := ioutil.TempDir("", ".dkenv") 23 | if err != nil { 24 | fmt.Println("Unable to create tmp dir for testing: %v", err) 25 | os.Exit(1) 26 | } 27 | 28 | testDkenvDir = tmpDir 29 | defer cleanUp(testDkenvDir) 30 | } 31 | 32 | func TestNew(t *testing.T) { 33 | c := New(&testBinDir, &testHomeDir, &testDkenvDir) 34 | 35 | assert.Equal(t, testBinDir, *c.BinDir) 36 | assert.Equal(t, testHomeDir, *c.HomeDir) 37 | assert.Equal(t, testDkenvDir, *c.DkenvDir) 38 | } 39 | 40 | func TestSetHomeDir(t *testing.T) { 41 | // Blank homedir - acquire it automatically (happy path) 42 | homeDir, homeDirErr := homedir.Dir() 43 | assert.NoError(t, homeDirErr) 44 | 45 | var blankDir string 46 | 47 | c1 := New(&testBinDir, &blankDir, &testDkenvDir) 48 | err1 := c1.setHomeDir() 49 | 50 | assert.NoError(t, err1) 51 | assert.Equal(t, homeDir, *c1.HomeDir) 52 | 53 | // Dir test with not-existent dir 54 | badDir := fmt.Sprintf("/tmp/bad_dir_%v", rand.Intn(99999)) 55 | c2 := New(&testBinDir, &badDir, &testDkenvDir) 56 | err2 := c2.setHomeDir() 57 | 58 | assert.Error(t, err2) 59 | 60 | // Verify right trim takes place 61 | homeDirWithSlash := homeDir + "/" 62 | 63 | c3 := New(&testBinDir, &homeDirWithSlash, &testDkenvDir) 64 | err3 := c3.setHomeDir() 65 | 66 | assert.NoError(t, err3) 67 | assert.Equal(t, homeDir, *c3.HomeDir) 68 | } 69 | 70 | func TestHandleBinDir(t *testing.T) { 71 | // Non-existent dir check 72 | badDir := fmt.Sprintf("/tmp/bad_dir_%v", rand.Intn(99999)) 73 | c1 := New(&badDir, &testHomeDir, &testDkenvDir) 74 | err1 := c1.handleBinDir() 75 | 76 | assert.Error(t, err1) 77 | 78 | // strip trailing slash check 79 | testBinDirWithSlash := testBinDir + "/" 80 | c2 := New(&testBinDirWithSlash, &testHomeDir, &testDkenvDir) 81 | err2 := c2.handleBinDir() 82 | 83 | assert.NoError(t, err2) 84 | assert.Equal(t, testBinDir, *c2.BinDir) 85 | } 86 | 87 | func TestHandleDkenvDir(t *testing.T) { 88 | // Create a tmp home dir 89 | tmpHomeDir, err := ioutil.TempDir("", "dkenv_temp_home_dir") 90 | if err != nil { 91 | t.Fatalf("Unable to create temp dir for testing: %v", err) 92 | } 93 | defer cleanUp(tmpHomeDir) 94 | 95 | // Happy path + auto create dkenv dir 96 | dkenvDir1 := "~/.dkenv" 97 | c1 := New(&testBinDir, &tmpHomeDir, &dkenvDir1) 98 | err1 := c1.handleDkenvDir() 99 | 100 | assert.NoError(t, err1) 101 | assert.Equal(t, tmpHomeDir+"/.dkenv", *c1.DkenvDir) 102 | 103 | // Assert directory has been created 104 | if _, err := os.Stat(*c1.DkenvDir); os.IsNotExist(err) { 105 | t.Errorf("%v not created after handleDkenvDir()", *c1.DkenvDir) 106 | } 107 | 108 | // Trailing slash check 109 | testDkenvDirWithSlash := testDkenvDir + "/" 110 | c2 := New(&testBinDir, &testHomeDir, &testDkenvDirWithSlash) 111 | err2 := c2.handleDkenvDir() 112 | 113 | assert.NoError(t, err2) 114 | assert.Equal(t, testDkenvDir, *c2.DkenvDir) 115 | 116 | // Test inability to create a dkenv dir 117 | tmpFile, err := ioutil.TempFile("", "dkenv_temp_file") 118 | if err != nil { 119 | t.Fatalf("Unable to create a temp file during testing: %v", err) 120 | } 121 | defer cleanUp(tmpFile.Name()) 122 | 123 | tmpFilename := tmpFile.Name() 124 | 125 | c3 := New(&testBinDir, &testHomeDir, &tmpFilename) 126 | err3 := c3.handleDkenvDir() 127 | assert.Error(t, err3) 128 | assert.Contains(t, err3.Error(), "Error creating dkenv dir") 129 | } 130 | 131 | func TestHandleArgs(t *testing.T) { 132 | homeDir, homeDirErr := homedir.Dir() 133 | assert.NoError(t, homeDirErr) 134 | 135 | // bad homedir should return error 136 | badDir := fmt.Sprintf("/tmp/bad_dir_%v", rand.Intn(99999)) 137 | 138 | c1 := New(&testBinDir, &badDir, &testDkenvDir) 139 | assert.Error(t, c1.HandleArgs()) 140 | 141 | // bad bindir should return error 142 | c2 := New(&badDir, &homeDir, &testDkenvDir) 143 | assert.Error(t, c2.HandleArgs()) 144 | 145 | // pre-existing tmp file for dkenv 146 | tmpFile, err := ioutil.TempFile("", "dkenv_temp_file") 147 | if err != nil { 148 | t.Fatalf("Unable to create a temp file during testing: %v", err) 149 | } 150 | defer cleanUp(tmpFile.Name()) 151 | 152 | tmpFilename := tmpFile.Name() 153 | 154 | c3 := New(&testBinDir, &homeDir, &tmpFilename) 155 | err3 := c3.HandleArgs() 156 | assert.Error(t, err3) 157 | assert.Contains(t, err3.Error(), "Error creating dkenv") 158 | 159 | // happy path 160 | c4 := New(&testBinDir, &homeDir, &testDkenvDir) 161 | assert.NoError(t, c4.HandleArgs()) 162 | } 163 | 164 | func TestIsDirAndExists(t *testing.T) { 165 | // Test non-existent dir 166 | badDir := fmt.Sprintf("/tmp/bad_dir_%v", rand.Intn(99999)) 167 | 168 | test1, err1 := IsDirAndExists(badDir) 169 | assert.NoError(t, err1) 170 | assert.False(t, test1) 171 | 172 | // Test w/ file (instead of dir) 173 | tmpFile, err2 := ioutil.TempFile("", "dkenv_temp_file") 174 | if err2 != nil { 175 | t.Fatalf("Unable to create a temp file during testing: %v", err2) 176 | } 177 | defer cleanUp(tmpFile.Name()) 178 | 179 | test2, err2 := IsDirAndExists(tmpFile.Name()) 180 | assert.NoError(t, err2, "Should get an error when given a file instead of dir") 181 | assert.False(t, test2) 182 | 183 | // Happy path 184 | tmpDir, err3 := ioutil.TempDir("", "dkenv_temp_dir") 185 | if err3 != nil { 186 | t.Fatalf("Unable to create temp dir for testing: %v", err3) 187 | } 188 | defer cleanUp(tmpDir) 189 | 190 | test3, err3 := IsDirAndExists(tmpDir) 191 | assert.NoError(t, err3) 192 | assert.True(t, test3) 193 | } 194 | 195 | func cleanUp(tmpDir string) { 196 | if err := os.RemoveAll(tmpDir); err != nil { 197 | fmt.Println("Unable to clean up tmp dir: %v", err) 198 | os.Exit(1) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /lib/downloader.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "runtime" 10 | 11 | log "github.com/Sirupsen/logrus" 12 | ) 13 | 14 | var ( 15 | errResource = errors.New("invalid webfinger resource") 16 | errNotYetImplemented = errors.New("Not yet implemented") 17 | errTooManyRedirects = errors.New("Too many redirects") 18 | errHTTPRedirect = errors.New("Redirect to non-https server") 19 | errHTTPCode = errors.New("Received unexpected http code") 20 | errSubjectMissmatch = errors.New("Subject doesn't match resource") 21 | ) 22 | 23 | // PassThru wraps an existing io.Reader. 24 | // 25 | // It simply forwards the Read() call, while displaying 26 | // the results from individual calls to it. 27 | type PassThru struct { 28 | io.Reader 29 | total int64 // Total # of bytes transferred 30 | length int64 // Expected length 31 | progress float64 32 | } 33 | 34 | func (d *Dkenv) DownloadDocker(version string) error { 35 | resp, err := d.getHttp(version) 36 | if err != nil { 37 | return err 38 | } 39 | defer resp.Body.Close() 40 | 41 | readerpt := &PassThru{Reader: resp.Body, length: resp.ContentLength} 42 | 43 | body, err := ioutil.ReadAll(readerpt) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | contentType := http.DetectContentType(body) 49 | if contentType != "application/octet-stream" { 50 | return fmt.Errorf("Content-Type mismatch: %s detected", contentType) 51 | } 52 | 53 | if err := ioutil.WriteFile(d.DkenvDir+"/docker-"+version, body, 0755); err != nil { 54 | return fmt.Errorf("Error(s) writing docker binary: %v", err) 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func (d *Dkenv) getHttp(version string) (*http.Response, error) { 61 | client := &http.Client{ 62 | CheckRedirect: redirectPolicyFunc, 63 | } 64 | 65 | var system string 66 | 67 | switch { 68 | case runtime.GOOS == "windows": 69 | system = "Windows" 70 | case runtime.GOOS == "linux": 71 | system = "Linux" 72 | case runtime.GOOS == "darwin": 73 | system = "Darwin" 74 | default: 75 | return nil, fmt.Errorf("Unsupported system type - %v", runtime.GOOS) 76 | } 77 | 78 | resp, err := client.Get("https://get.docker.com/builds/" + system + "/x86_64/docker-" + version) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | if resp.StatusCode != 200 { 84 | return nil, fmt.Errorf("No such docker version '%v'", version) 85 | } 86 | 87 | return resp, nil 88 | 89 | } 90 | 91 | func (pt *PassThru) Read(p []byte) (int, error) { 92 | n, err := pt.Reader.Read(p) 93 | if n > 0 { 94 | pt.total += int64(n) 95 | percentage := float64(pt.total) / float64(pt.length) * float64(100) 96 | 97 | if percentage-pt.progress > 2 { 98 | log.Debugf("Transferred %d percent", int(percentage)) 99 | pt.progress = percentage 100 | } 101 | } 102 | 103 | return n, err 104 | } 105 | 106 | func redirectPolicyFunc(req *http.Request, via []*http.Request) error { 107 | if len(via) > 10 { 108 | return errTooManyRedirects 109 | } 110 | 111 | if req.URL.Scheme != "https" { 112 | return errHTTPRedirect 113 | } 114 | 115 | return nil 116 | } 117 | -------------------------------------------------------------------------------- /lib/lib.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "math/rand" 8 | "os" 9 | "regexp" 10 | 11 | log "github.com/Sirupsen/logrus" 12 | ) 13 | 14 | var ( 15 | apiVersions = map[string]string{ 16 | "1.12": "1.0.1", 17 | "1.13": "1.1.2", 18 | "1.14": "1.2.0", 19 | "1.15": "1.3.3", 20 | "1.16": "1.4.1", 21 | "1.17": "1.5.0", 22 | "1.18": "1.6.0", 23 | "1.19": "1.7.1", 24 | "1.20": "1.8.3", 25 | "1.21": "1.9.1", 26 | "1.22": "1.10.3", 27 | } 28 | ) 29 | 30 | type Dkenv struct { 31 | DkenvDir string 32 | BinDir string 33 | } 34 | 35 | func New(dkenvDir, binDir string) *Dkenv { 36 | return &Dkenv{ 37 | DkenvDir: dkenvDir, 38 | BinDir: binDir, 39 | } 40 | } 41 | 42 | func (d *Dkenv) ListAction() error { 43 | installed, err := d.listInstalled() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if len(installed) == 0 { 49 | log.Warning("No installed Docker binaries found!") 50 | return nil 51 | } else { 52 | log.Infof("Found %v installed docker binaries", len(installed)) 53 | log.Info("") // blank line, for the pretty 54 | } 55 | 56 | for i := 0; i < len(installed); i++ { 57 | log.Infof("%v: %v", i+1, installed[i]) 58 | } 59 | 60 | return nil 61 | } 62 | 63 | func (d *Dkenv) FetchVersionAction(version string, api bool) error { 64 | clientVersion := version 65 | 66 | if api { 67 | var err error 68 | 69 | clientVersion, err = ApiToVersion(version) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | log.Infof("Found client '%v' for API version '%v'", clientVersion, version) 75 | } 76 | 77 | if !d.isInstalled(clientVersion) { 78 | // Attempt to download it 79 | log.Infof("Docker version %v not found - attempting to download...", clientVersion) 80 | 81 | if err := d.DownloadDocker(clientVersion); err != nil { 82 | return err 83 | } 84 | } else { 85 | log.Infof("Docker version %v already installed!", clientVersion) 86 | } 87 | 88 | // Update symlink 89 | if err := d.UpdateSymlink(clientVersion); err != nil { 90 | return err 91 | } 92 | 93 | return nil 94 | } 95 | 96 | // Return a list of found docker binaries 97 | func (d *Dkenv) listInstalled() ([]string, error) { 98 | fileList, err := ioutil.ReadDir(d.DkenvDir) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | found := make([]string, 0) 104 | 105 | for _, filename := range fileList { 106 | match, _ := regexp.MatchString(`docker-.+`, filename.Name()) 107 | if match { 108 | found = append(found, filename.Name()) 109 | } 110 | } 111 | 112 | return found, nil 113 | } 114 | 115 | func (d *Dkenv) isInstalled(version string) bool { 116 | if _, err := os.Stat(d.DkenvDir + "/docker-" + version); err == nil { 117 | return true 118 | } 119 | 120 | return false 121 | } 122 | 123 | // Symlink docker to a dkenv docker binary 124 | // 125 | // - If destination exists AND is *not* a symlink: 126 | // - backup destination file 127 | // - create symlink 128 | // - If destination exists AND *is* a symlink: 129 | // - check if the symlink already matches destination (noop if it does) 130 | // - remove and overwrite if symlink is incorrect 131 | // - If destination does not exist: 132 | // - create symlink 133 | // 134 | // TODO This will be a pain to test - refactor when possible. 135 | func (d *Dkenv) UpdateSymlink(version string) error { 136 | src := d.DkenvDir + "/docker-" + version 137 | dst := d.BinDir + "/docker" 138 | 139 | // Verify that the src file actually exists 140 | if _, err := os.Stat(src); err != nil { 141 | return fmt.Errorf("Unable to lookup source binary '%v': %v", src, err) 142 | } 143 | 144 | // Check if file exists; bail if real error 145 | _, err := os.Stat(dst) 146 | if err != nil && !os.IsNotExist(err) { 147 | return fmt.Errorf("Problems verifying existing docker symlink: %v", err) 148 | } 149 | 150 | // File does not exist, let's create it 151 | if err != nil && os.IsNotExist(err) { 152 | log.Infof("Creating symlink for '%v' -> '%v'", dst, src) 153 | 154 | if err := os.Symlink(src, dst); err != nil { 155 | return fmt.Errorf("Unable to create new docker symlink: %v", err) 156 | } 157 | 158 | return nil 159 | } 160 | 161 | // File exists; check if symlink 162 | fi, err := os.Lstat(dst) 163 | if err != nil { 164 | return fmt.Errorf("Unable to stat existing docker file '%v': %v", dst, err) 165 | } 166 | 167 | if fi.Mode()&os.ModeSymlink == os.ModeSymlink { 168 | tmpDst, err := os.Readlink(dst) 169 | if err != nil { 170 | return fmt.Errorf("Unable to lookup link info for existing docker symlink: %v", err) 171 | } 172 | 173 | if tmpDst == src { 174 | log.Infof("'%v' already pointing to '%v' - nothing to do!", dst, src) 175 | return nil 176 | } 177 | 178 | if err := os.Remove(dst); err != nil { 179 | return fmt.Errorf("Unable to remove old symlink: %v", err) 180 | } 181 | } else { 182 | backupName := dst + fmt.Sprintf(".%v.dkenv", rand.Intn(9999)) 183 | 184 | log.Infof("Backing up existing docker file %v to %v", dst, backupName) 185 | 186 | if err := os.Rename(dst, backupName); err != nil { 187 | return fmt.Errorf("Unable to backup existing docker file: %v", err) 188 | } 189 | } 190 | 191 | // Create/overwrite old symlink 192 | log.Infof("Creating symlink for '%v' -> '%v'", dst, src) 193 | 194 | if err := os.Symlink(src, dst); err != nil { 195 | return fmt.Errorf("Unable to create new docker symlink: %v", err) 196 | } 197 | 198 | return nil 199 | } 200 | 201 | func ApiToVersion(version string) (string, error) { 202 | if val, ok := apiVersions[version]; ok { 203 | return val, nil 204 | } 205 | 206 | return "", errors.New("Invalid API Version") 207 | } 208 | -------------------------------------------------------------------------------- /lib/lib_test.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | var ( 13 | // Set during init() 14 | testDkenvDir = "" 15 | testBinDir = "" 16 | ) 17 | 18 | // Create temp dirs necessary for tests 19 | func init() { 20 | tmpBinDir, err := ioutil.TempDir("", "tmp_dkenv_bin") 21 | if err != nil { 22 | fmt.Println("Unable to create tmp dir for testing: %v", err) 23 | os.Exit(1) 24 | } 25 | 26 | testBinDir = tmpBinDir 27 | defer cleanUp(testDkenvDir) 28 | 29 | tmpDkenvDir, err := ioutil.TempDir("", "tmp_dkenv_dir") 30 | if err != nil { 31 | fmt.Println("Unable to create tmp dir for testing: %v", err) 32 | os.Exit(1) 33 | } 34 | 35 | testDkenvDir = tmpDkenvDir 36 | defer cleanUp(testDkenvDir) 37 | 38 | } 39 | 40 | func TestNew(t *testing.T) { 41 | c := New(testDkenvDir, testBinDir) 42 | assert.IsType(t, &Dkenv{}, c) 43 | assert.Equal(t, testDkenvDir, c.DkenvDir) 44 | assert.Equal(t, testBinDir, c.BinDir) 45 | } 46 | 47 | func TestListAction(t *testing.T) { 48 | 49 | } 50 | 51 | func TestListInstalled(t *testing.T) { 52 | 53 | } 54 | 55 | func TestIsInstalled(t *testing.T) { 56 | 57 | } 58 | 59 | func TestFetchVersionAction(t *testing.T) { 60 | 61 | } 62 | 63 | func TestApiToVersion(t *testing.T) { 64 | 65 | } 66 | 67 | func TestUpdateSymlink(t *testing.T) { 68 | 69 | } 70 | 71 | func cleanUp(tmpDir string) { 72 | if err := os.RemoveAll(tmpDir); err != nil { 73 | fmt.Println("Unable to clean up tmp dir: %v", err) 74 | os.Exit(1) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/newrelic/dkenv/cli" 5 | "github.com/newrelic/dkenv/lib" 6 | 7 | log "github.com/Sirupsen/logrus" 8 | "gopkg.in/alecthomas/kingpin.v2" 9 | ) 10 | 11 | const ( 12 | VERSION = "1.1.1" 13 | DEFAULT_BINDIR = "/usr/local/bin" 14 | DEFAULT_DKENV_DIR = "~/.dkenv" 15 | ) 16 | 17 | var ( 18 | binDir = kingpin.Flag("bindir", "Directory to create symlinks for Docker binaries").Default(DEFAULT_BINDIR).String() 19 | homeDir = kingpin.Flag("homedir", "Override automatically found homedir").String() 20 | dkenvDir = kingpin.Flag("dkenvdir", "Directory to store Docker binaries").Default(DEFAULT_DKENV_DIR).String() 21 | debug = kingpin.Flag("debug", "Enable debug output").Short('d').Bool() 22 | 23 | // Commands 24 | client = kingpin.Command("client", "Download/switch Docker binary by *client* version") 25 | clientArg = client.Arg("version", "Docker client version").Required().String() 26 | api = kingpin.Command("api", "Download/switch Docker binary by *API* version") 27 | apiArg = api.Arg("version", "Docker API version").Required().String() 28 | 29 | // Not sure if there is any other, non-hacky way to set a command with no 30 | // args and retain the ability to see if it was called. 31 | list = kingpin.Command("list", "List downloaded/existing Docker binaries").Action(func(c *kingpin.ParseContext) error { 32 | listAction = true 33 | return nil 34 | }) 35 | 36 | listAction = false 37 | ) 38 | 39 | func init() { 40 | kingpin.Version(VERSION) 41 | kingpin.Parse() 42 | 43 | c := cli.New(binDir, homeDir, dkenvDir) 44 | if err := c.HandleArgs(); err != nil { 45 | log.Fatalf("Argument error: %v", err) 46 | } 47 | 48 | if *debug { 49 | log.SetLevel(log.DebugLevel) 50 | } 51 | } 52 | 53 | func main() { 54 | d := lib.New(*dkenvDir, *binDir) 55 | 56 | var err error 57 | 58 | switch { 59 | case listAction: 60 | err = d.ListAction() 61 | case *apiArg != "": 62 | err = d.FetchVersionAction(*apiArg, true) 63 | case *clientArg != "": 64 | err = d.FetchVersionAction(*clientArg, false) 65 | } 66 | 67 | if err != nil { 68 | log.Fatalf(err.Error()) 69 | } 70 | } 71 | --------------------------------------------------------------------------------