/
snapshot_testcmd.go
114 lines (96 loc) · 3.7 KB
/
snapshot_testcmd.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
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"time"
"github.com/google/go-cmp/cmp"
"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/sourcegraph/lib/output"
"github.com/sourcegraph/src-cli/internal/api"
"github.com/sourcegraph/src-cli/internal/instancehealth"
)
// note that this file is called '_testcmd.go' because '_test.go' cannot be used
func init() {
usage := `'src snapshot test' uses exported summary data to validate a restored and upgraded instance.
USAGE
src login # site-admin authentication required
src [-v] snapshot test [-summary-path="./src-snapshot-summary.json"]
ASSESSING RESULTS
The outputs of the test is supposed to be used only as a reference indicator of whether or not the instance is in a healthy state.
It generally does not give a definitive result, though if an issue is found that the test deems may be critical, the command will exit with status 1.
SNAPSHOT SUMMARY
The snapshot summary to assert against ('-summary-path') should be generated by 'src snapshot summary'.
TEST DATA
Use '-dump-requests' to see what data is requested to perform tests.
`
flagSet := flag.NewFlagSet("test", flag.ExitOnError)
snapshotSummaryPath := flagSet.String("summary-path", srcSnapshotSummaryPath, "path to read snapshot summary from")
since := flagSet.Duration("since", 1*time.Hour, "duration ago to look for healthcheck data")
apiFlags := api.NewFlags(flagSet)
snapshotCommands = append(snapshotCommands, &command{
flagSet: flagSet,
handler: func(args []string) error {
if err := flagSet.Parse(args); err != nil {
return err
}
out := output.NewOutput(flagSet.Output(), output.OutputOpts{Verbose: *verbose})
client := cfg.apiClient(apiFlags, flagSet.Output())
// Fetch health data
instanceHealth, err := instancehealth.GetIndicators(context.Background(), client)
if err != nil {
return err
}
// Optionally validate snapshots
if *snapshotSummaryPath != "" {
f, err := os.OpenFile(*snapshotSummaryPath, os.O_RDONLY, os.ModePerm)
if err != nil {
return errors.Wrap(err, "open snapshot file")
}
var recordedSummary snapshotSummary
if err := json.NewDecoder(f).Decode(&recordedSummary); err != nil {
return errors.Wrap(err, "read snapshot file")
}
// Fetch new snapshot
newSummary, err := fetchSnapshotSummary(context.Background(), client)
if err != nil {
return errors.Wrap(err, "get snapshot")
}
if err := compareSnapshotSummaries(out, recordedSummary, *newSummary); err != nil {
return err
}
}
// generate checks set
checks := instancehealth.NewChecks(*since, *instanceHealth)
// Run checks
var validationErrors error
for _, check := range checks {
validationErrors = errors.Append(validationErrors, check(out))
}
if validationErrors != nil {
out.WriteLine(output.Linef(output.EmojiFailure, output.StyleFailure,
"Critical issues found: %s", validationErrors.Error()))
return errors.New("validation failed")
}
out.WriteLine(output.Line(output.EmojiSuccess, output.StyleSuccess,
"No critical issues found!"))
return nil
},
usageFunc: func() { _, _ = fmt.Fprint(flag.CommandLine.Output(), usage) },
})
}
func compareSnapshotSummaries(out *output.Output, recordedSummary, newSummary snapshotSummary) error {
b := out.Block(output.Styled(output.StyleBold, "Snapshot contents"))
defer b.Close()
// Compare
diff := cmp.Diff(recordedSummary, newSummary)
if diff != "" {
b.WriteLine(output.Line(output.EmojiFailure, output.StyleFailure, "Snapshot diff detected:"))
_ = b.WriteCode("diff", diff)
return errors.New("snapshot mismatch")
}
b.WriteLine(output.Emoji(output.EmojiSuccess, "Snapshots match!"))
return nil
}