@@ -3,16 +3,16 @@ package main
3
3
import (
4
4
"context"
5
5
"fmt"
6
- stdlog "log"
7
6
"os"
8
7
"os/signal"
9
8
"runtime"
10
- "strconv"
11
9
"strings"
12
10
13
- "github.com/alecthomas/kingpin "
11
+ "github.com/fsnotify/fsnotify "
14
12
mtLog "github.com/mongodb/mongo-tools/common/log"
15
13
"github.com/mongodb/mongo-tools/common/options"
14
+ "github.com/spf13/cobra"
15
+ "github.com/spf13/viper"
16
16
17
17
"github.com/percona/percona-backup-mongodb/pbm/connect"
18
18
"github.com/percona/percona-backup-mongodb/pbm/errors"
@@ -23,84 +23,157 @@ import (
23
23
const mongoConnFlag = "mongodb-uri"
24
24
25
25
func main () {
26
+ rootCmd := rootCommand ()
27
+ rootCmd .AddCommand (versionCommand ())
28
+
29
+ if err := rootCmd .Execute (); err != nil {
30
+ fmt .Println (err )
31
+ }
32
+ }
33
+
34
+ func rootCommand () * cobra.Command {
35
+ rootCmd := & cobra.Command {
36
+ Use : "pbm-agent" ,
37
+ Short : "Percona Backup for MongoDB" ,
38
+ PreRunE : func (cmd * cobra.Command , args []string ) error {
39
+ if err := loadConfig (); err != nil {
40
+ return err
41
+ }
42
+ return validateRootCommand ()
43
+ },
44
+ Run : func (cmd * cobra.Command , args []string ) {
45
+ url := "mongodb://" + strings .Replace (viper .GetString (mongoConnFlag ), "mongodb://" , "" , 1 )
46
+
47
+ hidecreds ()
48
+
49
+ logOpts := buildLogOpts ()
50
+
51
+ l := log .NewWithOpts (nil , "" , "" , logOpts ).NewDefaultEvent ()
52
+
53
+ err := runAgent (url , viper .GetInt ("backup.dump-parallel-collections" ), logOpts )
54
+ if err != nil {
55
+ l .Error ("Exit: %v" , err )
56
+ os .Exit (1 )
57
+ }
58
+ l .Info ("Exit: <nil>" )
59
+ },
60
+ }
61
+
62
+ setRootFlags (rootCmd )
63
+ return rootCmd
64
+ }
65
+
66
+ func loadConfig () error {
67
+ cfgFile := viper .GetString ("config" )
68
+ if cfgFile == "" {
69
+ return nil
70
+ }
71
+
72
+ viper .SetConfigFile (cfgFile )
73
+ if err := viper .ReadInConfig (); err != nil {
74
+ return errors .New ("failed to read config: " + err .Error ())
75
+ }
76
+
77
+ viper .WatchConfig ()
78
+ return nil
79
+ }
80
+
81
+ func validateRootCommand () error {
82
+ if viper .GetString (mongoConnFlag ) == "" {
83
+ return errors .New ("required flag " + mongoConnFlag + " not set" )
84
+ }
85
+
86
+ if ! isValidLogLevel (viper .GetString ("log.level" )) {
87
+ return errors .New ("invalid log level" )
88
+ }
89
+
90
+ return nil
91
+ }
92
+
93
+ func setRootFlags (rootCmd * cobra.Command ) {
94
+ rootCmd .Flags ().StringP ("config" , "f" , "" , "Path to the config file" )
95
+ _ = viper .BindPFlag ("config" , rootCmd .Flags ().Lookup ("config" ))
96
+
97
+ rootCmd .Flags ().String (mongoConnFlag , "" , "MongoDB connection string" )
98
+ _ = viper .BindPFlag (mongoConnFlag , rootCmd .Flags ().Lookup (mongoConnFlag ))
99
+ _ = viper .BindEnv (mongoConnFlag , "PBM_MONGODB_URI" )
100
+
101
+ rootCmd .Flags ().Int ("dump-parallel-collections" , 0 , "Number of collections to dump in parallel" )
102
+ _ = viper .BindPFlag ("backup.dump-parallel-collections" , rootCmd .Flags ().Lookup ("dump-parallel-collections" ))
103
+ _ = viper .BindEnv ("backup.dump-parallel-collections" , "PBM_DUMP_PARALLEL_COLLECTIONS" )
104
+ viper .SetDefault ("backup.dump-parallel-collections" , runtime .NumCPU ()/ 2 )
105
+
106
+ rootCmd .Flags ().String ("log-path" , "" , "Path to file" )
107
+ _ = viper .BindPFlag ("log.path" , rootCmd .Flags ().Lookup ("log-path" ))
108
+ _ = viper .BindEnv ("log.path" , "LOG_PATH" )
109
+ viper .SetDefault ("log.path" , "/dev/stderr" )
110
+
111
+ rootCmd .Flags ().Bool ("log-json" , false , "Enable JSON logging" )
112
+ _ = viper .BindPFlag ("log.json" , rootCmd .Flags ().Lookup ("log-json" ))
113
+ _ = viper .BindEnv ("log.json" , "LOG_JSON" )
114
+ viper .SetDefault ("log.json" , false )
115
+
116
+ rootCmd .Flags ().String ("log-level" , "" ,
117
+ "Minimal log level based on severity level: D, I, W, E or F, low to high." +
118
+ "Choosing one includes higher levels too." )
119
+ _ = viper .BindPFlag ("log.level" , rootCmd .Flags ().Lookup ("log-level" ))
120
+ _ = viper .BindEnv ("log.level" , "LOG_LEVEL" )
121
+ viper .SetDefault ("log.level" , log .D )
122
+ }
123
+
124
+ func versionCommand () * cobra.Command {
26
125
var (
27
- pbmCmd = kingpin .New ("pbm-agent" , "Percona Backup for MongoDB" )
28
- pbmAgentCmd = pbmCmd .Command ("run" , "Run agent" ).
29
- Default ().
30
- Hidden ()
31
-
32
- mURI = pbmAgentCmd .Flag (mongoConnFlag , "MongoDB connection string" ).
33
- Envar ("PBM_MONGODB_URI" ).
34
- Required ().
35
- String ()
36
- dumpConns = pbmAgentCmd .
37
- Flag ("dump-parallel-collections" , "Number of collections to dump in parallel" ).
38
- Envar ("PBM_DUMP_PARALLEL_COLLECTIONS" ).
39
- Default (strconv .Itoa (runtime .NumCPU () / 2 )).
40
- Int ()
41
-
42
- versionCmd = pbmCmd .Command ("version" , "PBM version info" )
43
- versionShort = versionCmd .Flag ("short" , "Only version info" ).
44
- Default ("false" ).
45
- Bool ()
46
- versionCommit = versionCmd .Flag ("commit" , "Only git commit info" ).
47
- Default ("false" ).
48
- Bool ()
49
- versionFormat = versionCmd .Flag ("format" , "Output format <json or \" \" >" ).
50
- Default ("" ).
51
- String ()
52
-
53
- logPath = pbmCmd .Flag ("log-path" , "Path to file" ).
54
- Envar ("LOG_PATH" ).
55
- Default ("/dev/stderr" ).
56
- String ()
57
- logJSON = pbmCmd .Flag ("log-json" , "Enable JSON output" ).
58
- Envar ("LOG_JSON" ).
59
- Bool ()
60
- logLevel = pbmCmd .Flag (
61
- "log-level" ,
62
- "Minimal log level based on severity level: D, I, W, E or F, low to high. Choosing one includes higher levels too." ).
63
- Envar ("LOG_LEVEL" ).
64
- Default (log .D ).
65
- Enum (log .D , log .I , log .W , log .E , log .F )
126
+ versionShort bool
127
+ versionCommit bool
128
+ versionFormat string
66
129
)
67
130
68
- cmd , err := pbmCmd .DefaultEnvars ().Parse (os .Args [1 :])
69
- if err != nil && cmd != versionCmd .FullCommand () {
70
- stdlog .Println ("Error: Parse command line parameters:" , err )
71
- return
131
+ versionCmd := & cobra.Command {
132
+ Use : "version" ,
133
+ Short : "PBM version info" ,
134
+ Run : func (cmd * cobra.Command , args []string ) {
135
+ switch {
136
+ case versionShort :
137
+ cmd .Println (version .Current ().Short ())
138
+ case versionCommit :
139
+ cmd .Println (version .Current ().GitCommit )
140
+ default :
141
+ cmd .Println (version .Current ().All (versionFormat ))
142
+ }
143
+ },
72
144
}
73
145
74
- if cmd == versionCmd .FullCommand () {
75
- switch {
76
- case * versionCommit :
77
- fmt .Println (version .Current ().GitCommit )
78
- case * versionShort :
79
- fmt .Println (version .Current ().Short ())
80
- default :
81
- fmt .Println (version .Current ().All (* versionFormat ))
146
+ versionCmd .Flags ().BoolVar (& versionShort , "short" , false , "Only version info" )
147
+ versionCmd .Flags ().BoolVar (& versionCommit , "commit" , false , "Only git commit info" )
148
+ versionCmd .Flags ().StringVar (& versionFormat , "format" , "" , "Output format <json or \" \" >" )
149
+
150
+ return versionCmd
151
+ }
152
+
153
+ func isValidLogLevel (logLevel string ) bool {
154
+ validLogLevels := []string {log .D , log .I , log .W , log .E , log .F }
155
+
156
+ for _ , validLevel := range validLogLevels {
157
+ if logLevel == validLevel {
158
+ return true
82
159
}
83
- return
84
160
}
85
161
86
- // hidecreds() will rewrite the flag content, so we have to make a copy before passing it on
87
- url := "mongodb://" + strings .Replace (* mURI , "mongodb://" , "" , 1 )
88
-
89
- hidecreds ()
162
+ return false
163
+ }
90
164
91
- logOpts := & log.Opts {
92
- LogPath : * logPath ,
93
- LogLevel : * logLevel ,
94
- LogJSON : * logJSON ,
165
+ func buildLogOpts () * log.Opts {
166
+ logLevel := viper .GetString ("log.level" )
167
+ if ! isValidLogLevel (logLevel ) {
168
+ fmt .Printf ("Invalid log level: %s. Falling back to default.\n " , logLevel )
169
+ logLevel = log .D
95
170
}
96
- l := log .NewWithOpts (nil , "" , "" , logOpts ).NewDefaultEvent ()
97
171
98
- err = runAgent ( url , * dumpConns , logOpts )
99
- if err != nil {
100
- l . Error ( "Exit: %v" , err )
101
- os . Exit ( 1 )
172
+ return & log. Opts {
173
+ LogPath : viper . GetString ( "log.path" ),
174
+ LogLevel : logLevel ,
175
+ LogJSON : viper . GetBool ( "log.json" ),
102
176
}
103
- l .Info ("Exit: <nil>" )
104
177
}
105
178
106
179
func runAgent (
@@ -133,6 +206,13 @@ func runAgent(
133
206
logOpts )
134
207
defer logger .Close ()
135
208
209
+ viper .OnConfigChange (func (e fsnotify.Event ) {
210
+ logger .SetOpts (buildLogOpts ())
211
+ newOpts := logger .Opts ()
212
+ logger .Printf ("log options updated: log-path=%s, log-level:%s, log-json:%t" ,
213
+ newOpts .LogPath , newOpts .LogLevel , newOpts .LogJSON )
214
+ })
215
+
136
216
ctx = log .SetLoggerToContext (ctx , logger )
137
217
138
218
mtLog .SetDateFormat (log .LogTimeFormat )
0 commit comments