Skip to content

Commit 0f9908c

Browse files
authored
make cli use /api/endpoints (#121)
make cli use /api/endpoints instead of local static configuration
1 parent 89584b3 commit 0f9908c

4 files changed

Lines changed: 102 additions & 47 deletions

File tree

README.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ Note: This is not an official Google product.
1515
gcping [options...]
1616
1717
Options:
18-
-n Number of requests to be made to each region.
19-
By default 10; can't be negative.
20-
-c Max number of requests to be made at any time.
21-
By default 10; can't be negative or zero.
22-
-r Report latency for an individual region.
23-
-t Timeout. By default, no timeout.
24-
Examples: "500ms", "1s", "1s500ms".
25-
-top If true, only the top (non-global) region is printed.
26-
27-
-csv CSV output; disables verbose output.
28-
-v Verbose output.
18+
-n Number of requests to be made to each region.
19+
By default 10; can't be negative.
20+
-c Max number of requests to be made at any time.
21+
By default 10; can't be negative or zero.
22+
-r Report latency for an individual region.
23+
-t Timeout. By default, no timeout.
24+
Examples: "500ms", "1s", "1s500ms".
25+
-top If true, only the top (non-global) region is printed.
26+
-csv-cum If true, cumulative value is printed in CSV; disables default report.
27+
-url URL of endpoint list. Default is https://global.gcping.com/api/endpoints
28+
29+
-csv CSV output; disables verbose output.
30+
-v Verbose output.
2931
3032
Need a website version? See gcping.com
3133
```

internal/config/endpoints.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,56 @@
1414

1515
package config
1616

17+
import (
18+
"context"
19+
"encoding/json"
20+
"fmt"
21+
"net/http"
22+
)
23+
1724
// Endpoint represents a Cloud Run service deploy in a particular region.
1825
type Endpoint struct {
1926
// URL is the HTTPS URL of the service
2027
URL string
2128
// Region is the programmatic name of the region where the endpoint is
22-
// deloyed, e.g., us-central1.
29+
// deployed, e.g., us-central1.
2330
Region string
2431
// RegionName is the geographic name of the region, e.g., Iowa.
2532
RegionName string
2633
}
2734

35+
// EndpointsFromServer is used by the cli to generate an Endpoint map
36+
// using json served by the gcping endpoints.
37+
func EndpointsFromServer(ctx context.Context, endpointsURL string) (map[string]Endpoint, error) {
38+
39+
req, err := http.NewRequestWithContext(
40+
ctx,
41+
http.MethodGet,
42+
endpointsURL,
43+
nil,
44+
)
45+
if err != nil {
46+
return nil, err
47+
}
48+
resp, err := http.DefaultClient.Do(req)
49+
if err != nil {
50+
return nil, err
51+
}
52+
defer resp.Body.Close()
53+
54+
if resp.StatusCode != http.StatusOK {
55+
return nil, fmt.Errorf("%v %s", resp.Status, endpointsURL)
56+
}
57+
58+
e := make(map[string]Endpoint)
59+
decoder := json.NewDecoder(resp.Body)
60+
if err := decoder.Decode(&e); err != nil {
61+
return e, err
62+
}
63+
64+
return e, err
65+
}
66+
2867
// AllEndpoints associates a region name with its Cloud Run Endpoint.
2968
var AllEndpoints = map[string]Endpoint{
3069
"global": {

main.go

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package main
1717

1818
import (
19+
"context"
1920
"flag"
2021
"fmt"
2122
"net/http"
@@ -26,14 +27,15 @@ import (
2627
)
2728

2829
var (
29-
top bool
30-
number int // number of requests for each region
31-
concurrency int
32-
timeout time.Duration
33-
csv bool
34-
csvCum bool
35-
verbose bool
36-
region string
30+
top bool
31+
number int // number of requests for each region
32+
concurrency int
33+
timeout time.Duration
34+
csv bool
35+
csvCum bool
36+
verbose bool
37+
region string
38+
endpointsURL string
3739
// TODO(jbd): Add payload options such as body size.
3840

3941
client *http.Client // TODO(jbd): One client per worker?
@@ -48,10 +50,22 @@ func main() {
4850
flag.BoolVar(&csv, "csv", false, "")
4951
flag.BoolVar(&csvCum, "csv-cum", false, "")
5052
flag.StringVar(&region, "r", "", "")
53+
flag.StringVar(&endpointsURL, "url", "https://global.gcping.com/api/endpoints", "")
5154

5255
flag.Usage = usage
5356
flag.Parse()
5457

58+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
59+
defer cancel()
60+
61+
// Fetch and cache endpoint map in memory for the duration of the
62+
// process.
63+
endpoints, err := config.EndpointsFromServer(ctx, endpointsURL)
64+
if err != nil {
65+
fmt.Println(err)
66+
os.Exit(1)
67+
}
68+
5569
if number < 0 || concurrency <= 0 {
5670
usage()
5771
}
@@ -60,7 +74,7 @@ func main() {
6074
}
6175

6276
if region != "" {
63-
if _, found := config.AllEndpoints[region]; !found {
77+
if _, found := endpoints[region]; !found {
6478
fmt.Printf("region %q is not supported or does not exist\n", region)
6579
os.Exit(1)
6680
}
@@ -75,13 +89,13 @@ func main() {
7589

7690
switch {
7791
case region != "":
78-
w.reportRegion(region)
92+
w.reportRegion(endpoints, region)
7993
case top:
80-
w.reportTop()
94+
w.reportTop(endpoints)
8195
case csvCum:
82-
w.reportCSV()
96+
w.reportCSV(endpoints)
8397
default:
84-
w.reportAll()
98+
w.reportAll(endpoints)
8599
}
86100
}
87101

@@ -102,6 +116,7 @@ Options:
102116
Examples: "500ms", "1s", "1s500ms".
103117
-top If true, only the top (non-global) region is printed.
104118
-csv-cum If true, cumulative value is printed in CSV; disables default report.
119+
-url URL of endpoint list. Default is https://global.gcping.com/api/endpoints
105120
106121
-csv CSV output; disables verbose output.
107122
-v Verbose output.

worker.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ func (w *worker) start() {
108108
}
109109
}
110110

111-
func (w *worker) sortOutput() []output {
111+
func (w *worker) sortOutput(em map[string]config.Endpoint) []output {
112112
m := make(map[string]output)
113-
for i := 0; i < w.size(region); i++ {
113+
for i := 0; i < w.size(em, region); i++ {
114114
o := <-w.outputs
115115

116116
a := m[o.region]
@@ -133,17 +133,17 @@ func (w *worker) sortOutput() []output {
133133
return all
134134
}
135135

136-
func (w *worker) reportAll() {
136+
func (w *worker) reportAll(em map[string]config.Endpoint) {
137137
w.inputs = make(chan input, concurrency)
138-
w.outputs = make(chan output, w.size(region))
138+
w.outputs = make(chan output, w.size(em, region))
139139
for i := 0; i < number; i++ {
140-
for r, e := range config.AllEndpoints {
140+
for r, e := range em {
141141
w.inputs <- input{region: r, endpoint: e.URL}
142142
}
143143
}
144144
close(w.inputs)
145145

146-
sorted := w.sortOutput()
146+
sorted := w.sortOutput(em)
147147
tr := tabwriter.NewWriter(os.Stdout, 3, 2, 2, ' ', 0)
148148
for i, a := range sorted {
149149
fmt.Fprintf(tr, "%2d.\t[%v]\t%v", i+1, a.region, a.median())
@@ -155,59 +155,58 @@ func (w *worker) reportAll() {
155155
tr.Flush()
156156
}
157157

158-
func (w *worker) reportCSV() {
158+
func (w *worker) reportCSV(em map[string]config.Endpoint) {
159159
w.inputs = make(chan input, concurrency)
160-
w.outputs = make(chan output, w.size(region))
160+
w.outputs = make(chan output, w.size(em, region))
161161
for i := 0; i < number; i++ {
162-
for r, e := range config.AllEndpoints {
162+
for r, e := range em {
163163
w.inputs <- input{region: r, endpoint: e.URL}
164164
}
165165
}
166166
close(w.inputs)
167167

168-
sorted := w.sortOutput()
168+
sorted := w.sortOutput(em)
169169
fmt.Println("region,latency_ns,errors")
170170
for _, a := range sorted {
171171
fmt.Printf("%v,%v,%v\n", a.region, a.median().Nanoseconds(), a.errors)
172172
}
173173
}
174174

175-
func (w *worker) reportTop() {
175+
func (w *worker) reportTop(em map[string]config.Endpoint) {
176176
w.inputs = make(chan input, concurrency)
177-
w.outputs = make(chan output, w.size(region))
177+
w.outputs = make(chan output, w.size(em, region))
178178
for i := 0; i < number; i++ {
179-
for r, e := range config.AllEndpoints {
179+
for r, e := range em {
180180
w.inputs <- input{region: r, endpoint: e.URL}
181181
}
182182
}
183183
close(w.inputs)
184184

185-
sorted := w.sortOutput()
185+
sorted := w.sortOutput(em)
186186
t := sorted[0].region
187187
if t == "global" {
188188
t = sorted[1].region
189189
}
190-
fmt.Println(t )
190+
fmt.Println(t)
191191
return
192192
}
193193

194-
func (w *worker) reportRegion(region string) {
194+
func (w *worker) reportRegion(em map[string]config.Endpoint, region string) {
195195
w.inputs = make(chan input, concurrency)
196-
w.outputs = make(chan output, w.size(region))
196+
w.outputs = make(chan output, w.size(em, region))
197197
for i := 0; i < number; i++ {
198-
e, _ := config.AllEndpoints[region]
198+
e, _ := em[region]
199199
w.inputs <- input{region: region, endpoint: e.URL}
200200
}
201201
close(w.inputs)
202202

203-
sorted := w.sortOutput()
203+
sorted := w.sortOutput(em)
204204
fmt.Println(sorted[0].median())
205-
206205
}
207206

208-
func (w *worker) size(region string) int {
207+
func (w *worker) size(em map[string]config.Endpoint, region string) int {
209208
if region != "" {
210209
return number
211210
}
212-
return number * len(config.AllEndpoints)
211+
return number * len(em)
213212
}

0 commit comments

Comments
 (0)