├── .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
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
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
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 | [](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 |
--------------------------------------------------------------------------------