This repository was archived by the owner on Aug 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 604
Expand file tree
/
Copy pathctlcli.go
More file actions
145 lines (119 loc) · 4.86 KB
/
ctlcli.go
File metadata and controls
145 lines (119 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// ctlcli holds the interfaces and helpers for the current CLI library
// (codeganster/cli).
//
// Abstracting away any cli library implementation from the commands themselves,
// keeping our commands testable and generic.
package ctlcli
import (
"fmt"
"io"
"github.com/codegangsta/cli"
"github.com/koding/logging"
)
var closers []io.Closer
// CloseFunc wraps func to provide an implementation for the io.Closer interface.
type CloseFunc func() error
// Close implements the io.Closer interface.
func (c CloseFunc) Close() error { return c() }
// CloseOnExit is a hack to close program-lifetime-bound resources,
// like log file or BoltDB database.
func CloseOnExit(c io.Closer) {
closers = append(closers, c)
}
// Close is a hack to close program-lifetime-bound resources,
// like log file or BoltDB database.
func Close() {
for i := len(closers) - 1; i >= 0; i-- {
closers[i].Close()
}
}
// Command is the basic interface for all klientctl commands.
type Command interface {
// Run implements the main CLI function for running a command. Error is returned
// in combination with the exit status for easy API usage.
Run() (int, error)
// Help, presents help to the user. However, many CLI libraries take care of the
// Help presentation, such as formatting and args. This method usually calls back to
// the parent CLI library via an internal reference to the ctlcli.Helper type. See
// ctlcli.Helper docs for further explanation.
Help()
}
// AutocompleteCommand is an interface for a Command that wants to provide
// Autocomplete functionality.
type AutocompleteCommand interface {
// Autocomplete prints to Stdout what is to be autocompleted, one item per line.
// Handling of the autocompletion is done by the shell (bash/fish/etc), this
// method simply prints to Stdout.
Autocomplete(args ...string) error
}
// Helper implements an abstraction between Commands and codegansta/cli. How the
// helpers are implemented varies depending on the actual Helper function we're
// wrapping, but typically they write the given io.Writer to the cli.Context
// before calling the cli.ShowHelp-like commands.
type Helper func(io.Writer)
// ExitingCommand is a function that returns an exit code
type ExitingCommand func(*cli.Context, logging.Logger, string) int
// ExitingErrCommand is a function that returns an exit code and an error. Behavior
// is the same as ExitingCommand, but it also supports an error return.
type ExitingErrCommand func(*cli.Context, logging.Logger, string) (int, error)
// CommandFactory returns a struct implementing the Command interface.
type CommandFactory func(*cli.Context, logging.Logger, string) Command
// CommandHelper maps the codegansta/cli Help to our generic Helper type.
// It does so by calling cli.ShowCommandHelper after setting the proper writer. For
// reference, see:
//
// cli.ShowCommandHelp https://github.com/codegangsta/cli/blob/master/help.go#L104
//
// The context and command for this are typically provided by the command factory.
func CommandHelper(ctx *cli.Context, cmd string) Helper {
return func(w io.Writer) {
ctx.App.Writer = w
cli.ShowCommandHelp(ctx, cmd)
}
}
// ExitAction implements a cli.Command's Action field for an ExitingCommand type.
func ExitAction(f ExitingCommand, log logging.Logger, cmdName string) cli.ActionFunc {
eec := func(c *cli.Context, log logging.Logger, cmdName string) (int, error) {
return f(c, log, cmdName), nil
}
return ExitErrAction(eec, log, cmdName)
}
// FactoryAction implements a cli.Command's Action field.
func FactoryAction(factory CommandFactory, log logging.Logger, cmdName string) cli.ActionFunc {
eec := func(c *cli.Context, log logging.Logger, cmdName string) (int, error) {
return factory(c, log, cmdName).Run()
}
return ExitErrAction(eec, log, cmdName)
}
// ExitErrAction implements a cli.Command's Action field for an ExitingErrCommand
func ExitErrAction(f ExitingErrCommand, log logging.Logger, cmdName string) cli.ActionFunc {
return func(c *cli.Context) error {
defer Close()
exit, err := f(c, log, cmdName)
if err != nil || exit != 0 {
log.Error("Command %q encountered error. Exit:%d, err:%v", cmdName, exit, err)
msg := fmt.Sprintf("error executing %q command", cmdName)
if err != nil {
msg = msg + ": " + err.Error()
}
// Print error message to the user.
return cli.NewExitError(msg, exit)
}
return nil
}
}
// FactoryCompletion implements codeganstas cli.Command's bash completion field
func FactoryCompletion(factory CommandFactory, log logging.Logger, cmdName string) cli.BashCompleteFunc {
return func(c *cli.Context) {
cmd := factory(c, log, cmdName)
// If the command implements AutocompleteCommand, run the autocomplete.
if aCmd, ok := cmd.(AutocompleteCommand); ok {
if err := aCmd.Autocomplete(c.Args()...); err != nil {
log.Error(
"Autocompletion of a command encountered error. command:%s, err:%s",
cmdName, err,
)
}
}
}
}