Skip to content

Commit b1b539f

Browse files
committed
record
Signed-off-by: Harper, Jason M <[email protected]>
1 parent 9693ca7 commit b1b539f

File tree

2 files changed

+114
-20
lines changed

2 files changed

+114
-20
lines changed

cmd/config/config.go

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"perfspect/internal/report"
1515
"perfspect/internal/script"
1616
"perfspect/internal/target"
17+
"perfspect/internal/util"
1718
"slices"
1819
"strings"
1920

@@ -52,6 +53,22 @@ func runCmd(cmd *cobra.Command, args []string) error {
5253
// appContext is the application context that holds common data and resources.
5354
appContext := cmd.Parent().Context().Value(common.AppContext{}).(common.AppContext)
5455
localTempDir := appContext.LocalTempDir
56+
outputDir := appContext.OutputDir
57+
58+
flagRecord := cmd.Flags().Lookup(flagRecordName).Value.String() == "true"
59+
flagNoSummary := cmd.Flags().Lookup(flagNoSummaryName).Value.String() == "true"
60+
61+
// create output directory if we are recording the configuration
62+
if flagRecord {
63+
err := util.CreateDirectoryIfNotExists(outputDir, 0755) // #nosec G301
64+
if err != nil {
65+
err = fmt.Errorf("failed to create output directory: %w", err)
66+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
67+
slog.Error(err.Error())
68+
cmd.SilenceUsage = true
69+
return err
70+
}
71+
}
5572
// get the targets
5673
myTargets, targetErrs, err := common.GetTargets(cmd, true, true, localTempDir)
5774
if err != nil {
@@ -91,14 +108,40 @@ func runCmd(cmd *cobra.Command, args []string) error {
91108
cmd.SilenceUsage = true
92109
return err
93110
}
94-
// print config prior to changes, optionally
95-
if !cmd.Flags().Lookup(flagNoSummaryName).Changed {
96-
if err := printConfig(myTargets, localTempDir); err != nil {
111+
// collect and print and/or record the configuration before making changes
112+
if !flagNoSummary || flagRecord {
113+
config, err := getConfig(myTargets, localTempDir)
114+
if err != nil {
115+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
116+
slog.Error(err.Error())
117+
cmd.SilenceUsage = true
118+
return err
119+
}
120+
reports, err := processConfig(config)
121+
if err != nil {
122+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
123+
slog.Error(err.Error())
124+
cmd.SilenceUsage = true
125+
return err
126+
}
127+
filesWritten, err := printConfig(reports, !flagNoSummary, flagRecord, outputDir)
128+
if err != nil {
97129
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
98130
slog.Error(err.Error())
99131
cmd.SilenceUsage = true
100132
return err
101133
}
134+
if len(filesWritten) > 0 {
135+
message := "Configuration"
136+
if len(filesWritten) > 1 {
137+
message = "Configurations"
138+
}
139+
fmt.Printf("%s recorded:\n", message)
140+
for _, fileWritten := range filesWritten {
141+
fmt.Printf(" %s\n", fileWritten)
142+
}
143+
fmt.Println()
144+
}
102145
}
103146
// if no changes were requested, print a message and return
104147
var changeRequested bool
@@ -138,9 +181,24 @@ func runCmd(cmd *cobra.Command, args []string) error {
138181
}
139182
multiSpinner.Finish()
140183
fmt.Println() // blank line
141-
// print config after making changes
142-
if !cmd.Flags().Lookup(flagNoSummaryName).Changed {
143-
if err := printConfig(myTargets, localTempDir); err != nil {
184+
// collect and print the configuration before making changes
185+
if !flagNoSummary {
186+
config, err := getConfig(myTargets, localTempDir)
187+
if err != nil {
188+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
189+
slog.Error(err.Error())
190+
cmd.SilenceUsage = true
191+
return err
192+
}
193+
reports, err := processConfig(config)
194+
if err != nil {
195+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
196+
slog.Error(err.Error())
197+
cmd.SilenceUsage = true
198+
return err
199+
}
200+
_, err = printConfig(reports, !flagNoSummary, false, outputDir) // print, don't record
201+
if err != nil {
144202
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
145203
slog.Error(err.Error())
146204
cmd.SilenceUsage = true
@@ -227,7 +285,8 @@ func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGr
227285
channelError <- nil
228286
}
229287

230-
func printConfig(myTargets []target.Target, localTempDir string) (err error) {
288+
// getConfig collects the configuration data from the target(s)
289+
func getConfig(myTargets []target.Target, localTempDir string) ([]common.TargetScriptOutputs, error) {
231290
scriptNames := report.GetScriptNamesForTable(report.ConfigurationTableName)
232291
var scriptsToRun []script.ScriptDefinition
233292
for _, scriptName := range scriptNames {
@@ -239,10 +298,10 @@ func printConfig(myTargets []target.Target, localTempDir string) (err error) {
239298
channelTargetScriptOutputs := make(chan common.TargetScriptOutputs)
240299
channelError := make(chan error)
241300
for _, myTarget := range myTargets {
242-
err = multiSpinner.AddSpinner(myTarget.GetName())
301+
err := multiSpinner.AddSpinner(myTarget.GetName())
243302
if err != nil {
244303
err = fmt.Errorf("failed to add spinner: %v", err)
245-
return
304+
return nil, err
246305
}
247306
// run the selected scripts on the target
248307
go collectOnTarget(myTarget, scriptsToRun, localTempDir, channelTargetScriptOutputs, channelError, multiSpinner.Status)
@@ -269,28 +328,55 @@ func printConfig(myTargets []target.Target, localTempDir string) (err error) {
269328
}
270329
}
271330
multiSpinner.Finish()
272-
// process and print the table for each target
273-
for _, targetScriptOutputs := range orderedTargetScriptOutputs {
331+
return orderedTargetScriptOutputs, nil
332+
}
333+
334+
// processConfig processes the collected configuration data and creates text reports
335+
func processConfig(targetScriptOutputs []common.TargetScriptOutputs) (map[string][]byte, error) {
336+
reports := make(map[string][]byte)
337+
var err error
338+
for _, targetScriptOutput := range targetScriptOutputs {
274339
// process the tables, i.e., get field values from raw script output
275340
tableNames := []string{report.ConfigurationTableName}
276341
var tableValues []report.TableValues
277-
if tableValues, err = report.ProcessTables(tableNames, targetScriptOutputs.ScriptOutputs); err != nil {
342+
if tableValues, err = report.ProcessTables(tableNames, targetScriptOutput.ScriptOutputs); err != nil {
278343
err = fmt.Errorf("failed to process collected data: %v", err)
279-
return
344+
return nil, err
280345
}
281346
// create the report for this single table
282347
var reportBytes []byte
283-
if reportBytes, err = report.Create("txt", tableValues, targetScriptOutputs.TargetName); err != nil {
348+
if reportBytes, err = report.Create("txt", tableValues, targetScriptOutput.TargetName); err != nil {
284349
err = fmt.Errorf("failed to create report: %v", err)
285-
return
350+
return nil, err
286351
}
287-
// print the report
288-
if len(orderedTargetScriptOutputs) > 1 {
289-
fmt.Printf("%s\n", targetScriptOutputs.TargetName)
352+
// append the report to the list
353+
reports[targetScriptOutput.TargetName] = reportBytes
354+
}
355+
return reports, nil
356+
}
357+
358+
// printConfig prints and/or saves the configuration reports
359+
func printConfig(reports map[string][]byte, toStdout bool, toFile bool, outputDir string) ([]string, error) {
360+
filesWriten := []string{}
361+
for targetName, reportBytes := range reports {
362+
if toStdout {
363+
// print the report to stdout
364+
if len(reports) > 1 {
365+
fmt.Printf("%s\n", targetName)
366+
}
367+
fmt.Print(string(reportBytes))
368+
}
369+
if toFile {
370+
outputFilePath := fmt.Sprintf("%s/config_%s.txt", outputDir, targetName)
371+
err := os.WriteFile(outputFilePath, reportBytes, 0644) // #nosec G306
372+
if err != nil {
373+
err = fmt.Errorf("failed to write configuration report to file: %v", err)
374+
return filesWriten, err
375+
}
376+
filesWriten = append(filesWriten, outputFilePath)
290377
}
291-
fmt.Print(string(reportBytes))
292378
}
293-
return
379+
return filesWriten, nil
294380
}
295381

296382
// collectOnTarget runs the scripts on the target and sends the results to the appropriate channels

cmd/config/flag_groups.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const (
6666
// other flag names
6767
const (
6868
flagNoSummaryName = "no-summary"
69+
flagRecordName = "record"
70+
flagRestoreName = "restore"
6971
)
7072

7173
// governorOptions - list of valid governor options
@@ -237,6 +239,12 @@ func initializeFlags(cmd *cobra.Command) {
237239
group.flags = append(group.flags,
238240
newBoolFlag(cmd, flagNoSummaryName, false, nil, "do not print configuration summary", "", nil),
239241
)
242+
group.flags = append(group.flags,
243+
newBoolFlag(cmd, flagRecordName, false, nil, "record the current configuration to a file to be restored later", "", nil),
244+
)
245+
group.flags = append(group.flags,
246+
newStringFlag(cmd, flagRestoreName, "", nil, "restore a previously recorded configuration from the specified file", "", nil),
247+
)
240248
flagGroups = append(flagGroups, group)
241249

242250
common.AddTargetFlags(Cmd)

0 commit comments

Comments
 (0)