11package cmd
22
33import (
4+ "context"
45 "fmt"
56 "strings"
67
78 "github.com/google/uuid"
89 "github.com/spf13/cobra"
910 "github.com/spf13/viper"
11+ "go.opentelemetry.io/otel/codes"
1012
13+ "github.com/databacker/api/go/api"
1114 "github.com/databacker/mysql-backup/pkg/compression"
1215 "github.com/databacker/mysql-backup/pkg/core"
1316 "github.com/databacker/mysql-backup/pkg/storage"
17+ "github.com/databacker/mysql-backup/pkg/util"
1418)
1519
1620const (
@@ -38,92 +42,84 @@ func dumpCmd(passedExecs execs, cmdConfig *cmdConfiguration) (*cobra.Command, er
3842 bindFlags (cmd , v )
3943 },
4044 RunE : func (cmd * cobra.Command , args []string ) error {
45+ ctx := context .Background ()
46+ // this is the tracer that we will use throughout the entire run
47+ tracer := getTracer ("dump" )
48+ ctx = util .ContextWithTracer (ctx , tracer )
49+ _ , startupSpan := tracer .Start (ctx , "startup" )
4150 cmdConfig .logger .Debug ("starting dump" )
51+ defer func () {
52+ tp := getTracerProvider ()
53+ tp .ForceFlush (ctx )
54+ _ = tp .Shutdown (ctx )
55+ }()
4256 // check targets
4357 targetURLs := v .GetStringSlice ("target" )
4458 var (
45- targets []storage. Storage
46- err error
59+ dumpConfig * api. Dump
60+ scriptsConfig * api. Scripts
4761 )
48- if len (targetURLs ) > 0 {
49- for _ , t := range targetURLs {
50- store , err := storage .ParseURL (t , cmdConfig .creds )
51- if err != nil {
52- return fmt .Errorf ("invalid target url: %v" , err )
53- }
54- targets = append (targets , store )
55- }
56- } else {
57- // try the config file
58- if cmdConfig .configuration != nil {
59- // parse the target objects, then the ones listed for the backup
60- targetStructures := cmdConfig .configuration .Targets
61- dumpTargets := cmdConfig .configuration .Dump .Targets
62- for _ , t := range dumpTargets {
63- var store storage.Storage
64- if target , ok := targetStructures [t ]; ! ok {
65- return fmt .Errorf ("target %s from dump configuration not found in targets configuration" , t )
66- } else {
67- store , err = target .Storage .Storage ()
68- if err != nil {
69- return fmt .Errorf ("target %s from dump configuration has invalid URL: %v" , t , err )
70- }
71- }
72- targets = append (targets , store )
73- }
62+ if cmdConfig .configuration != nil {
63+ dumpConfig = cmdConfig .configuration .Dump
64+ if dumpConfig != nil {
65+ scriptsConfig = dumpConfig .Scripts
7466 }
7567 }
68+ targets , err := parseTargets (targetURLs , cmdConfig )
69+ if err != nil {
70+ return fmt .Errorf ("error parsing targets: %v" , err )
71+ }
7672 if len (targets ) == 0 {
7773 return fmt .Errorf ("no targets specified" )
7874 }
7975 safechars := v .GetBool ("safechars" )
80- if ! v .IsSet ("safechars" ) && cmdConfig . configuration != nil {
81- safechars = cmdConfig . configuration . Dump .Safechars
76+ if ! v .IsSet ("safechars" ) && dumpConfig != nil && dumpConfig . Safechars != nil {
77+ safechars = * dumpConfig .Safechars
8278 }
8379 include := v .GetStringSlice ("include" )
84- if len (include ) == 0 && cmdConfig . configuration != nil {
85- include = cmdConfig . configuration . Dump .Include
80+ if len (include ) == 0 && dumpConfig != nil && dumpConfig . Include != nil {
81+ include = * dumpConfig .Include
8682 }
8783 // make this slice nil if it's empty, so it is consistent; used mainly for test consistency
8884 if len (include ) == 0 {
8985 include = nil
9086 }
9187 exclude := v .GetStringSlice ("exclude" )
92- if len (exclude ) == 0 && cmdConfig . configuration != nil {
93- exclude = cmdConfig . configuration . Dump .Exclude
88+ if len (exclude ) == 0 && dumpConfig != nil && dumpConfig . Exclude != nil {
89+ exclude = * dumpConfig .Exclude
9490 }
9591 // make this slice nil if it's empty, so it is consistent; used mainly for test consistency
9692 if len (exclude ) == 0 {
9793 exclude = nil
9894 }
9995 preBackupScripts := v .GetString ("pre-backup-scripts" )
100- if preBackupScripts == "" && cmdConfig . configuration != nil {
101- preBackupScripts = cmdConfig . configuration . Dump . Scripts .PreBackup
96+ if preBackupScripts == "" && scriptsConfig != nil && scriptsConfig . PreBackup != nil {
97+ preBackupScripts = * scriptsConfig .PreBackup
10298 }
10399 postBackupScripts := v .GetString ("post-backup-scripts" )
104- if postBackupScripts == "" && cmdConfig . configuration != nil {
105- postBackupScripts = cmdConfig . configuration . Dump . Scripts .PostBackup
100+ if postBackupScripts == "" && scriptsConfig != nil && scriptsConfig . PostBackup != nil {
101+ postBackupScripts = * scriptsConfig .PostBackup
106102 }
107103 noDatabaseName := v .GetBool ("no-database-name" )
108- if ! v .IsSet ("no-database-name" ) && cmdConfig . configuration != nil {
109- noDatabaseName = cmdConfig . configuration . Dump .NoDatabaseName
104+ if ! v .IsSet ("no-database-name" ) && dumpConfig != nil && dumpConfig . NoDatabaseName != nil {
105+ noDatabaseName = * dumpConfig .NoDatabaseName
110106 }
111107 compact := v .GetBool ("compact" )
112- if ! v .IsSet ("compact" ) && cmdConfig . configuration != nil {
113- compact = cmdConfig . configuration . Dump .Compact
108+ if ! v .IsSet ("compact" ) && dumpConfig != nil && dumpConfig . Compact != nil {
109+ compact = * dumpConfig .Compact
114110 }
115111 maxAllowedPacket := v .GetInt ("max-allowed-packet" )
116- if ! v .IsSet ("max-allowed-packet" ) && cmdConfig . configuration != nil && cmdConfig . configuration . Dump .MaxAllowedPacket != 0 {
117- maxAllowedPacket = cmdConfig . configuration . Dump .MaxAllowedPacket
112+ if ! v .IsSet ("max-allowed-packet" ) && dumpConfig != nil && dumpConfig . MaxAllowedPacket != nil && * dumpConfig .MaxAllowedPacket != 0 {
113+ maxAllowedPacket = * dumpConfig .MaxAllowedPacket
118114 }
119115
120116 // compression algorithm: check config, then CLI/env var overrides
121117 var (
122118 compressionAlgo string
123119 compressor compression.Compressor
124120 )
125- if cmdConfig .configuration != nil {
126- compressionAlgo = cmdConfig . configuration . Dump .Compression
121+ if cmdConfig .configuration != nil && dumpConfig . Compression != nil {
122+ compressionAlgo = * dumpConfig .Compression
127123 }
128124 compressionVar := v .GetString ("compression" )
129125 if compressionVar != "" {
@@ -138,41 +134,21 @@ func dumpCmd(passedExecs execs, cmdConfig *cmdConfiguration) (*cobra.Command, er
138134
139135 // retention, if enabled
140136 retention := v .GetString ("retention" )
141- if retention == "" && cmdConfig .configuration != nil {
142- retention = cmdConfig .configuration .Prune .Retention
137+ if retention == "" && cmdConfig .configuration != nil && cmdConfig . configuration . Prune . Retention != nil {
138+ retention = * cmdConfig .configuration .Prune .Retention
143139 }
144140 filenamePattern := v .GetString ("filename-pattern" )
145141
146- if ! v .IsSet ("filename-pattern" ) && cmdConfig . configuration != nil {
147- filenamePattern = cmdConfig . configuration . Dump .FilenamePattern
142+ if ! v .IsSet ("filename-pattern" ) && dumpConfig != nil && dumpConfig . FilenamePattern != nil {
143+ filenamePattern = * dumpConfig .FilenamePattern
148144 }
149145 if filenamePattern == "" {
150146 filenamePattern = defaultFilenamePattern
151147 }
152148
153149 // timer options
154- once := v .GetBool ("once" )
155- if ! v .IsSet ("once" ) && cmdConfig .configuration != nil {
156- once = cmdConfig .configuration .Dump .Schedule .Once
157- }
158- cron := v .GetString ("cron" )
159- if cron == "" && cmdConfig .configuration != nil {
160- cron = cmdConfig .configuration .Dump .Schedule .Cron
161- }
162- begin := v .GetString ("begin" )
163- if begin == "" && cmdConfig .configuration != nil {
164- begin = cmdConfig .configuration .Dump .Schedule .Begin
165- }
166- frequency := v .GetInt ("frequency" )
167- if frequency == 0 && cmdConfig .configuration != nil {
168- frequency = cmdConfig .configuration .Dump .Schedule .Frequency
169- }
170- timerOpts := core.TimerOptions {
171- Once : once ,
172- Cron : cron ,
173- Begin : begin ,
174- Frequency : frequency ,
175- }
150+ timerOpts := parseTimerOptions (v , cmdConfig .configuration )
151+
176152 var executor execs
177153 executor = & core.Executor {}
178154 if passedExecs != nil {
@@ -182,7 +158,14 @@ func dumpCmd(passedExecs execs, cmdConfig *cmdConfiguration) (*cobra.Command, er
182158
183159 // at this point, any errors should not have usage
184160 cmd .SilenceUsage = true
161+
162+ // done with the startup
163+ startupSpan .End ()
164+
185165 if err := executor .Timer (timerOpts , func () error {
166+ // start a new span for the dump, should not be a child of the startup one
167+ tracerCtx , dumpSpan := tracer .Start (ctx , "run" )
168+ defer dumpSpan .End ()
186169 uid := uuid .New ()
187170 dumpOpts := core.DumpOptions {
188171 Targets : targets ,
@@ -199,15 +182,18 @@ func dumpCmd(passedExecs execs, cmdConfig *cmdConfiguration) (*cobra.Command, er
199182 Run : uid ,
200183 FilenamePattern : filenamePattern ,
201184 }
202- _ , err := executor .Dump (dumpOpts )
185+ _ , err := executor .Dump (tracerCtx , dumpOpts )
203186 if err != nil {
187+ dumpSpan .SetStatus (codes .Error , fmt .Sprintf ("error running dump: %v" , err ))
204188 return fmt .Errorf ("error running dump: %w" , err )
205189 }
206190 if retention != "" {
207- if err := executor .Prune (core.PruneOptions {Targets : targets , Retention : retention }); err != nil {
191+ if err := executor .Prune (tracerCtx , core.PruneOptions {Targets : targets , Retention : retention }); err != nil {
192+ dumpSpan .SetStatus (codes .Error , fmt .Sprintf ("error running prune: %v" , err ))
208193 return fmt .Errorf ("error running prune: %w" , err )
209194 }
210195 }
196+ dumpSpan .SetStatus (codes .Ok , "dump complete" )
211197 return nil
212198 }); err != nil {
213199 return fmt .Errorf ("error running command: %w" , err )
@@ -278,3 +264,80 @@ S3: If it is a URL of the format s3://bucketname/path then it will connect via S
278264
279265 return cmd , nil
280266}
267+
268+ func parseTimerOptions (v * viper.Viper , config * api.ConfigSpec ) core.TimerOptions {
269+ var scheduleConfig * api.Schedule
270+ if config != nil {
271+ dumpConfig := config .Dump
272+ if dumpConfig != nil {
273+ scheduleConfig = dumpConfig .Schedule
274+ }
275+ }
276+ once := v .GetBool ("once" )
277+ if ! v .IsSet ("once" ) && scheduleConfig != nil && scheduleConfig .Once != nil {
278+ once = * scheduleConfig .Once
279+ }
280+ cron := v .GetString ("cron" )
281+ if cron == "" && scheduleConfig != nil && scheduleConfig .Cron != nil {
282+ cron = * scheduleConfig .Cron
283+ }
284+ begin := v .GetString ("begin" )
285+ if begin == "" && scheduleConfig != nil && scheduleConfig .Begin != nil {
286+ begin = fmt .Sprintf ("%d" , * scheduleConfig .Begin )
287+ }
288+ frequency := v .GetInt ("frequency" )
289+ if frequency == 0 && scheduleConfig != nil && scheduleConfig .Frequency != nil {
290+ frequency = * scheduleConfig .Frequency
291+ }
292+ return core.TimerOptions {
293+ Once : once ,
294+ Cron : cron ,
295+ Begin : begin ,
296+ Frequency : frequency ,
297+ }
298+
299+ }
300+
301+ func parseTargets (urls []string , cmdConfig * cmdConfiguration ) ([]storage.Storage , error ) {
302+ var targets []storage.Storage
303+ if len (urls ) > 0 {
304+ for _ , t := range urls {
305+ store , err := storage .ParseURL (t , cmdConfig .creds )
306+ if err != nil {
307+ return nil , fmt .Errorf ("invalid target url: %v" , err )
308+ }
309+ targets = append (targets , store )
310+ }
311+ } else {
312+ // try the config file
313+ if cmdConfig .configuration != nil {
314+ // parse the target objects, then the ones listed for the backup
315+ var (
316+ targetStructures map [string ]api.Target
317+ dumpTargets []string
318+ )
319+ if cmdConfig .configuration .Targets != nil {
320+ targetStructures = * cmdConfig .configuration .Targets
321+ }
322+ if cmdConfig .configuration != nil && cmdConfig .configuration .Dump != nil && cmdConfig .configuration .Dump .Targets != nil {
323+ dumpTargets = * cmdConfig .configuration .Dump .Targets
324+ }
325+ for _ , t := range dumpTargets {
326+ var (
327+ store storage.Storage
328+ err error
329+ )
330+ if target , ok := targetStructures [t ]; ! ok {
331+ return nil , fmt .Errorf ("target %s from dump configuration not found in targets configuration" , t )
332+ } else {
333+ store , err = storage .FromTarget (target )
334+ if err != nil {
335+ return nil , fmt .Errorf ("target %s from dump configuration has invalid URL: %v" , t , err )
336+ }
337+ }
338+ targets = append (targets , store )
339+ }
340+ }
341+ }
342+ return targets , nil
343+ }
0 commit comments