-
Notifications
You must be signed in to change notification settings - Fork 19
/
main.go
200 lines (177 loc) · 5.33 KB
/
main.go
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"code.cloudfoundry.org/grootfs/commands"
"code.cloudfoundry.org/grootfs/commands/config"
"code.cloudfoundry.org/grootfs/store"
"code.cloudfoundry.org/lager/v3"
"github.com/containers/storage/pkg/reexec"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
const (
defaultTardisBin = "tardis"
defaultNewuidmapBin = "newuidmap"
defaultNewgidmapBin = "newgidmap"
)
func init() {
if reexec.Init() {
os.Exit(0)
}
}
func main() {
grootfs := cli.NewApp()
grootfs.Name = "grootfs"
grootfs.Version = version
grootfs.Usage = "I am Groot!"
/* This block is copied from here:
https://github.com/cloudfoundry/grootfs/blob/e3748cdda45b5e7efe500e442f9ae029499a287d/vendor/github.com/urfave/cli/v2/errors.go#L94-L116
Here is why:
The default ExitErrHandler is always printing error messagees to stderr. However, Grootfs is using stderr
for logging unless a log file is supplied, and error messages are expected by the test runner on stdout. Given that
the default handler does not allow configuration of the error stream we have created one that is identical, but writes
error messges to stdout to keep our tests happy.
*/
grootfs.ExitErrHandler = func(ctx *cli.Context, err error) {
if err == nil {
return
}
if exitErr, ok := err.(cli.ExitCoder); ok {
if err.Error() != "" {
if _, ok := exitErr.(cli.ErrorFormatter); ok {
_, _ = fmt.Fprintf(grootfs.Writer, "%+v\n", err)
} else {
_, _ = fmt.Fprintln(grootfs.Writer, err)
}
}
cli.OsExiter(exitErr.ExitCode())
return
}
}
grootfs.Flags = []cli.Flag{
&cli.StringFlag{
Name: "config",
Usage: "Path to config file",
},
&cli.StringFlag{
Name: "store",
Usage: "Path to the store directory",
Value: store.DefaultStorePath,
},
&cli.StringFlag{
Name: "log-level",
Usage: "Set logging level <debug|info|error|fatal>",
Value: "fatal",
},
&cli.StringFlag{
Name: "log-file",
Usage: "File to write logs to. Using this option sets the log level to `info` if --log-level is not specified.",
},
&cli.StringFlag{
Name: "log-timestamp-format",
Usage: "The format to use for timestamps in logs. Can be 'unix-epoch' or 'rfc3339'",
},
&cli.StringFlag{
Name: "tardis-bin",
Usage: "Path to tardis bin. (If not provided will use $PATH)",
Value: defaultTardisBin,
},
&cli.StringFlag{
Name: "newuidmap-bin",
Usage: "Path to newuidmap bin. (If not provided will use $PATH)",
Value: defaultNewuidmapBin,
},
&cli.StringFlag{
Name: "newgidmap-bin",
Usage: "Path to newgidmap bin. (If not provided will use $PATH)",
Value: defaultNewgidmapBin,
},
&cli.StringFlag{
Name: "metron-endpoint",
Usage: "Metron endpoint used to send metrics",
Value: "",
},
}
grootfs.Commands = []*cli.Command{
&commands.InitStoreCommand,
&commands.DeleteStoreCommand,
&commands.GenerateVolumeSizeMetadata,
&commands.CreateCommand,
&commands.DeleteCommand,
&commands.StatsCommand,
&commands.CleanCommand,
&commands.ListCommand,
&commands.CapacityCommand,
}
grootfs.Before = func(ctx *cli.Context) error {
cfgBuilder, err := config.NewBuilder(ctx.String("config"))
if err != nil {
return cli.Exit(err.Error(), 1)
}
ctx.App.Metadata["configBuilder"] = cfgBuilder
cfg, err := cfgBuilder.WithStorePath(ctx.String("store"), ctx.IsSet("store")).
WithTardisBin(ctx.String("tardis-bin"), ctx.IsSet("tardis-bin")).
WithMetronEndpoint(ctx.String("metron-endpoint")).
WithLogLevel(ctx.String("log-level"), ctx.IsSet("log-level")).
WithLogFile(ctx.String("log-file")).
WithLogTimestampFormat(ctx.String("log-timestamp-format")).
WithNewuidmapBin(ctx.String("newuidmap-bin"), ctx.IsSet("newuidmap-bin")).
WithNewgidmapBin(ctx.String("newgidmap-bin"), ctx.IsSet("newgidmap-bin")).
Build()
if err != nil {
return cli.Exit(err.Error(), 1)
}
lagerLogLevel := translateLogLevel(cfg.LogLevel)
logger, err := configureLogger(lagerLogLevel, cfg.LogFile, cfg.LogTimestampFormat)
if err != nil {
return cli.Exit(err.Error(), 1)
}
ctx.App.Metadata["logger"] = logger
// Sadness. We need to do that becuase we use stderr for logs so user
// errors need to end up in stdout.
cli.ErrWriter = os.Stdout
if err := os.Setenv("TMPDIR", filepath.Join(cfg.StorePath, store.TempDirName)); err != nil {
logger.Error("setting TMPDIR env var", err)
return cli.Exit(err.Error(), 1)
}
return nil
}
if err := grootfs.Run(os.Args); err != nil {
os.Exit(1)
}
}
func translateLogLevel(logLevel string) lager.LogLevel {
switch strings.ToUpper(logLevel) {
case "DEBUG":
return lager.DEBUG
case "INFO":
return lager.INFO
case "ERROR":
return lager.ERROR
default:
return lager.FATAL
}
}
func configureLogger(logLevel lager.LogLevel, logFile, logTimestampFormat string) (lager.Logger, error) {
logWriter := os.Stderr
if logFile != "" {
var err error
logWriter, err = os.OpenFile(logFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
return nil, errors.Wrap(err, "failed to configure logger")
}
if logLevel == lager.FATAL {
logLevel = lager.INFO
}
}
logger := lager.NewLogger("grootfs")
if logTimestampFormat == "rfc3339" {
logger.RegisterSink(lager.NewPrettySink(logWriter, logLevel))
} else {
logger.RegisterSink(lager.NewWriterSink(logWriter, logLevel))
}
return logger, nil
}