Skip to content

Commit c243d67

Browse files
Summarize: only run webserver on demand (#89)
* Only run webserver on demand for summarize. Output html if no server launched. Make default format markdown Signed-off-by: Rohit Nayak <rohit@planetscale.com> * Reuse summarize template, but different layout for standalone pages including style/js inline Signed-off-by: Rohit Nayak <rohit@planetscale.com> * refactor: move code around Signed-off-by: Andres Taylor <andres@planetscale.com> * test: improve CLI testing Signed-off-by: Andres Taylor <andres@planetscale.com> --------- Signed-off-by: Rohit Nayak <rohit@planetscale.com> Signed-off-by: Andres Taylor <andres@planetscale.com> Co-authored-by: Andres Taylor <andres@planetscale.com>
1 parent 1390f1c commit c243d67

File tree

10 files changed

+417
-168
lines changed

10 files changed

+417
-168
lines changed

go/cmd/keys.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func keysCmd() *cobra.Command {
3535
PreRun: func(cmd *cobra.Command, _ []string) {
3636
csvConfig = csvFlagsToConfig(cmd, *flags)
3737
},
38-
RunE: func(_ *cobra.Command, args []string) error {
38+
RunE: func(c *cobra.Command, args []string) error {
3939
cfg := keys.Config{
4040
FileName: args[0],
4141
}
@@ -46,7 +46,7 @@ func keysCmd() *cobra.Command {
4646
}
4747
cfg.Loader = loader
4848

49-
return keys.Run(cfg)
49+
return keys.Run(c.OutOrStdout(), cfg)
5050
},
5151
}
5252

go/cmd/main_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
Copyright 2024 The Vitess Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"bytes"
21+
"strings"
22+
"testing"
23+
24+
"github.com/stretchr/testify/require"
25+
)
26+
27+
func TestApp(t *testing.T) {
28+
tests := []struct {
29+
args []string
30+
want string
31+
}{
32+
{[]string{"help"}, "Utils tools for testing, running and benchmarking Vitess"},
33+
{[]string{"keys", "../../t/demo.test"}, `"queryStructure"`},
34+
{[]string{"keys", "--help"}, `Runs vexplain keys on all queries of the test file`},
35+
{[]string{"txs", "--help"}, `Analyze transactions on a query log`},
36+
{[]string{"test", "--help"}, `Test the given workload against both Vitess and MySQL`},
37+
{[]string{"trace", "--help"}, `Runs the given workload and does a`},
38+
{[]string{"summarize", "--help"}, `Compares and analyses a trace output`},
39+
{[]string{"dbinfo", "--help"}, `Loads info from the database including row counts`},
40+
{[]string{"planalyze", "--help"}, `Analyze the query plan`},
41+
}
42+
for _, tt := range tests {
43+
t.Run("vt "+strings.Join(tt.args, " "), func(t *testing.T) {
44+
cmd := getRootCmd()
45+
buf := new(bytes.Buffer)
46+
cmd.SetOut(buf)
47+
cmd.SetErr(buf)
48+
cmd.SetArgs(tt.args)
49+
50+
err := cmd.Execute()
51+
52+
out := buf.String()
53+
require.NoError(t, err, out)
54+
require.Contains(t, out, tt.want)
55+
})
56+
}
57+
}

go/cmd/root.go

Lines changed: 9 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,77 +17,34 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20-
"fmt"
2120
"os"
22-
"time"
2321

2422
"github.com/spf13/cobra"
25-
26-
"github.com/vitessio/vt/go/web"
2723
)
2824

2925
// Execute adds all child commands to the root command and sets flags appropriately.
3026
// This is called by main.main(). It only needs to happen once to the rootCmd.
3127
func Execute() {
28+
err := getRootCmd().Execute()
29+
if err != nil {
30+
os.Exit(1)
31+
}
32+
}
33+
34+
func getRootCmd() *cobra.Command {
3235
// rootCmd represents the base command when called without any subcommands
33-
var port int64
34-
webserverStarted := false
35-
ch := make(chan int, 2)
3636
root := &cobra.Command{
3737
Use: "vt",
3838
Short: "Utils tools for testing, running and benchmarking Vitess.",
39-
RunE: func(_ *cobra.Command, _ []string) error {
40-
if port > 0 {
41-
if webserverStarted {
42-
return nil
43-
}
44-
webserverStarted = true
45-
go startWebServer(port, ch)
46-
<-ch
47-
}
48-
return nil
49-
},
5039
}
51-
root.PersistentFlags().Int64VarP(&port, "port", "p", 8080, "Port to run the web server on")
5240
root.CompletionOptions.HiddenDefaultCmd = true
5341

54-
root.AddCommand(summarizeCmd(&port))
42+
root.AddCommand(summarizeCmd())
5543
root.AddCommand(testerCmd())
5644
root.AddCommand(tracerCmd())
5745
root.AddCommand(keysCmd())
5846
root.AddCommand(dbinfoCmd())
5947
root.AddCommand(transactionsCmd())
6048
root.AddCommand(planalyzeCmd())
61-
62-
if err := root.ParseFlags(os.Args[1:]); err != nil {
63-
panic(err)
64-
}
65-
66-
if !webserverStarted && port > 0 {
67-
webserverStarted = true
68-
go startWebServer(port, ch)
69-
} else {
70-
ch <- 1
71-
}
72-
73-
// FIXME: add sync b/w webserver and root command, for now just add a wait to make sure webserver is running
74-
time.Sleep(2 * time.Second)
75-
76-
err := root.Execute()
77-
if err != nil {
78-
fmt.Printf("Error: %v\n", err)
79-
os.Exit(1)
80-
}
81-
<-ch
82-
}
83-
84-
func startWebServer(port int64, ch chan int) {
85-
if port > 0 && port != 8080 {
86-
panic("(FIXME: make port configurable) Port is not 8080")
87-
}
88-
web.Run(port)
89-
if os.WriteFile("/dev/stderr", []byte("Web server is running, use Ctrl-C to exit\n"), 0o600) != nil {
90-
panic("Failed to write to /dev/stderr")
91-
}
92-
ch <- 1
49+
return root
9350
}

go/cmd/summarize.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import (
2222
"github.com/vitessio/vt/go/summarize"
2323
)
2424

25-
func summarizeCmd(port *int64) *cobra.Command {
25+
func summarizeCmd() *cobra.Command {
2626
var hotMetric string
2727
var showGraph bool
2828
var outputFormat string
29+
var launchWebServer bool
2930

3031
cmd := &cobra.Command{
3132
Use: "summarize old_file.json [new_file.json]",
@@ -34,13 +35,13 @@ func summarizeCmd(port *int64) *cobra.Command {
3435
Example: "vt summarize old.json new.json",
3536
Args: cobra.RangeArgs(1, 2),
3637
Run: func(_ *cobra.Command, args []string) {
37-
summarize.Run(args, hotMetric, showGraph, outputFormat, port)
38+
summarize.Run(args, hotMetric, showGraph, outputFormat, launchWebServer)
3839
},
3940
}
4041

4142
cmd.Flags().StringVar(&hotMetric, "hot-metric", "total-time", "Metric to determine hot queries (options: usage-count, total-rows-examined, avg-rows-examined, avg-time, total-time)")
4243
cmd.Flags().BoolVar(&showGraph, "graph", false, "Show the query graph in the browser")
43-
cmd.Flags().StringVar(&outputFormat, "format", "html", "Output format (options: html, markdown)")
44-
44+
cmd.Flags().StringVar(&outputFormat, "format", "markdown", "Output format (options: html, markdown)")
45+
cmd.Flags().BoolVar(&launchWebServer, "web", false, "Start a web server to view the summary")
4546
return cmd
4647
}

go/keys/keys.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"errors"
2222
"fmt"
2323
"io"
24-
"os"
2524
"sort"
2625

2726
querypb "vitess.io/vitess/go/vt/proto/query"
@@ -73,11 +72,7 @@ type (
7372
}
7473
)
7574

76-
func Run(cfg Config) error {
77-
return run(os.Stdout, cfg)
78-
}
79-
80-
func run(out io.Writer, cfg Config) error {
75+
func Run(out io.Writer, cfg Config) error {
8176
si := &SchemaInfo{
8277
Tables: make(map[string]Columns),
8378
}

go/keys/keys_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestKeys(t *testing.T) {
6565
for _, tcase := range cases {
6666
t.Run(tcase.expectedFile, func(t *testing.T) {
6767
sb := &strings.Builder{}
68-
err := run(sb, tcase.cfg)
68+
err := Run(sb, tcase.cfg)
6969
require.NoError(t, err)
7070

7171
out, err := os.ReadFile("../testdata/keys-output/" + tcase.expectedFile)

go/summarize/summarize.go

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package summarize
1818

1919
import (
20-
"encoding/json"
2120
"errors"
2221
"fmt"
2322
"io"
@@ -31,6 +30,7 @@ import (
3130
"golang.org/x/term"
3231

3332
"github.com/vitessio/vt/go/data"
33+
"github.com/vitessio/vt/go/web"
3434
)
3535

3636
type (
@@ -40,12 +40,22 @@ type (
4040
}
4141
)
4242

43+
type SummaryOutput struct {
44+
Summary
45+
DateOfAnalysis string
46+
}
47+
4348
type summaryWorker = func(s *Summary) error
4449

45-
func Run(files []string, hotMetric string, showGraph bool, outputFormat string, port *int64) {
50+
func Run(files []string, hotMetric string, showGraph bool, outputFormat string, launchWebServer bool) {
4651
var traces []traceSummary
4752
var workers []summaryWorker
4853

54+
if launchWebServer && outputFormat != "html" {
55+
fmt.Println("cannot use --web flag without --format=html")
56+
os.Exit(1)
57+
}
58+
4959
for _, file := range files {
5060
typ, err := data.GetFileType(file)
5161
exitIfError(err)
@@ -77,7 +87,7 @@ func Run(files []string, hotMetric string, showGraph bool, outputFormat string,
7787

7888
traceCount := len(traces)
7989
if traceCount <= 0 {
80-
s, err := printSummary(hotMetric, workers, outputFormat, port)
90+
s, err := printSummary(hotMetric, workers, outputFormat, launchWebServer)
8191
exitIfError(err)
8292
if showGraph {
8393
err := renderQueryGraph(s)
@@ -106,7 +116,7 @@ func exitIfError(err error) {
106116
os.Exit(1)
107117
}
108118

109-
func printSummary(hotMetric string, workers []summaryWorker, outputFormat string, port *int64) (*Summary, error) {
119+
func printSummary(hotMetric string, workers []summaryWorker, outputFormat string, launchWebServer bool) (*Summary, error) {
110120
s, err := NewSummary(hotMetric)
111121
if err != nil {
112122
return nil, err
@@ -118,25 +128,27 @@ func printSummary(hotMetric string, workers []summaryWorker, outputFormat string
118128
}
119129
}
120130
outputFormat = strings.ToLower(outputFormat)
121-
if *port == 0 && outputFormat == "html" {
122-
fmt.Println("port is required when output format is html")
123-
os.Exit(1)
124-
}
125131
switch outputFormat {
126132
case "html":
127-
summaryJSON, err := json.Marshal(*s)
128-
if err != nil {
129-
fmt.Println("Error marshalling summary:", err)
130-
return nil, err
133+
summarizeOutput := SummaryOutput{
134+
Summary: *s,
135+
DateOfAnalysis: time.Now().Format(time.DateTime),
131136
}
132137

133-
tmpFile, err := writeToTempFile(summaryJSON)
134-
if err != nil {
135-
return s, err
136-
}
137-
err = launchInBrowser(tmpFile)
138-
if err != nil {
139-
return s, err
138+
if launchWebServer {
139+
err = launchInBrowser(summarizeOutput)
140+
if err != nil {
141+
return s, err
142+
}
143+
} else {
144+
html, err := web.RenderFile("summarize.html", "layout_standalone.html", summarizeOutput)
145+
if err != nil {
146+
return nil, err
147+
}
148+
_, err = io.Copy(os.Stdout, html)
149+
if err != nil {
150+
return nil, err
151+
}
140152
}
141153
case "markdown":
142154
// Print the response
@@ -150,16 +162,30 @@ func printSummary(hotMetric string, workers []summaryWorker, outputFormat string
150162
return s, nil
151163
}
152164

153-
func launchInBrowser(tmpFile *os.File) error {
165+
func launchInBrowser(summarizeOutput SummaryOutput) error {
166+
html, err := web.RenderFile("summarize.html", "layout.html", summarizeOutput)
167+
if err != nil {
168+
return err
169+
}
170+
tmpFile, err := writeToTempFile(html.Bytes())
171+
if err != nil {
172+
return err
173+
}
174+
175+
ch := make(chan error)
176+
go func() {
177+
web.Run(8080)
178+
ch <- nil
179+
}()
154180
port := int64(8080) // FIXME: take this from flags
155181
url := fmt.Sprintf("http://localhost:%d/summarize?file=", port) + tmpFile.Name()
156-
err := exec.Command("open", url).Start()
182+
err = exec.Command("open", url).Start()
157183
if err != nil {
158184
fmt.Println("Error launching browser:", err)
159185
return err
160186
}
161187
fmt.Println("URL launched in default browser:", url)
162-
return nil
188+
return <-ch
163189
}
164190

165191
func writeToTempFile(summaryJSON []byte) (*os.File, error) {

0 commit comments

Comments
 (0)