@@ -3,9 +3,14 @@ package main
3
3
import (
4
4
"crypto/tls"
5
5
"crypto/x509"
6
+ "encoding/json"
6
7
"fmt"
8
+ "io"
9
+ "net/http"
7
10
"net/url"
8
11
"os"
12
+ "strconv"
13
+ "strings"
9
14
"time"
10
15
11
16
"code.cloudfoundry.org/tlsconfig"
@@ -14,30 +19,41 @@ import (
14
19
)
15
20
16
21
const USAGE = `Usage:
17
- /var/vcap/jobs/gorouter/bin/nats_client [COMMAND] [SUBJECT] [MESSAGE]
22
+ /var/vcap/jobs/gorouter/bin/nats_client [COMMAND]
18
23
19
24
COMMANDS:
20
- subscribe (Default) Streams NATS messages from server with provided SUBJECT. Default SUBJECT is 'router.*'
21
- Example: /var/vcap/jobs/gorouter/bin/nats_client subscribe 'router.*'
25
+ sub [SUBJECT] [MESSAGE]
26
+ (Default) Streams NATS messages from server with provided SUBJECT. Default SUBJECT is 'router.*'
27
+ Example: /var/vcap/jobs/gorouter/bin/nats_client sub 'router.*'
22
28
23
- publish Publish the provided message JSON to SUBJECT subscription. SUBJECT and MESSAGE are required
24
- Example: /var/vcap/jobs/gorouter/bin/nats_client publish router.register '{"host":"172.217.6.68","port":80,"uris":["bar.example.com"]}'
29
+ pub [SUBJECT] [MESSAGE]
30
+ Publish the provided message JSON to SUBJECT subscription. SUBJECT and MESSAGE are required
31
+ Example: /var/vcap/jobs/gorouter/bin/nats_client pub router.register '{"host":"172.217.6.68","port":80,"uris":["bar.example.com"]}'
32
+
33
+ save <FILE>
34
+ Save this gorouter's route table to a json file.
35
+ Example: /var/vcap/jobs/gorouter/bin/nats_client save routes.json'
36
+
37
+ load <FILE>
38
+ Load routes from a json file into this gorouter.
39
+ Example: /var/vcap/jobs/gorouter/bin/nats_client load routes.json'
25
40
`
26
41
27
42
// Simple NATS client for debugging
28
43
// Uses gorouter.yml for config
29
44
func main () {
30
- if os .Args [len (os .Args )- 1 ] == "--help" || os .Args [len (os .Args )- 1 ] == "-h" || os .Args [len (os .Args )- 1 ] == "help" {
45
+ //TODO: use a proper arg parser here
46
+ if len (os .Args ) < 2 || os .Args [len (os .Args )- 1 ] == "--help" || os .Args [len (os .Args )- 1 ] == "-h" || os .Args [len (os .Args )- 1 ] == "help" {
31
47
fmt .Println (USAGE )
32
48
os .Exit (1 )
33
49
}
34
50
35
51
configPath := os .Args [1 ]
36
- command := "subscribe "
52
+ command := "sub "
37
53
if len (os .Args ) >= 3 {
38
54
command = os .Args [2 ]
39
55
}
40
- if command != "subscribe " && command != "publish " {
56
+ if command != "sub " && command != "pub" && command != "save" && command != "load " {
41
57
fmt .Println (USAGE )
42
58
os .Exit (1 )
43
59
}
@@ -48,7 +64,7 @@ func main() {
48
64
}
49
65
50
66
var message string
51
- if command == "publish " {
67
+ if command == "pub " {
52
68
if len (os .Args ) >= 5 {
53
69
message = os .Args [4 ]
54
70
} else {
@@ -57,6 +73,16 @@ func main() {
57
73
}
58
74
}
59
75
76
+ var filename string
77
+ if command == "save" || command == "load" {
78
+ if len (os .Args ) >= 4 {
79
+ filename = os .Args [3 ]
80
+ } else {
81
+ fmt .Println (USAGE )
82
+ os .Exit (1 )
83
+ }
84
+ }
85
+
60
86
config , err := loadConfig (configPath )
61
87
if err != nil {
62
88
panic (err )
@@ -72,7 +98,7 @@ func main() {
72
98
panic (err )
73
99
}
74
100
75
- if command == "publish " {
101
+ if command == "pub " {
76
102
fmt .Fprintf (os .Stderr , "Publishing message to %s\n " , subject )
77
103
err := natsConn .Publish (subject , []byte (message ))
78
104
if err != nil {
@@ -81,7 +107,7 @@ func main() {
81
107
fmt .Fprintln (os .Stderr , "Done" )
82
108
}
83
109
84
- if command == "subscribe " {
110
+ if command == "sub " {
85
111
fmt .Fprintf (os .Stderr , "Subscribing to %s\n " , subject )
86
112
subscription , err := natsConn .SubscribeSync (subject )
87
113
if err != nil {
@@ -98,6 +124,25 @@ func main() {
98
124
}
99
125
}
100
126
}
127
+
128
+ if command == "save" {
129
+ fmt .Fprintf (os .Stderr , "Saving route table to %s\n " , filename )
130
+ err := dumpRoutes (config , filename )
131
+ if err != nil {
132
+ panic (err )
133
+ }
134
+ fmt .Fprintln (os .Stderr , "Done" )
135
+ }
136
+
137
+ if command == "load" {
138
+ fmt .Fprintf (os .Stderr , "Loading route table from %s\n " , filename )
139
+ err := loadRoutes (config , filename )
140
+ if err != nil {
141
+ panic (err )
142
+ }
143
+ fmt .Fprintln (os .Stderr , "Done" )
144
+ }
145
+
101
146
}
102
147
103
148
// From code.cloudfoundry.org/gorouter/mbus/client.go
@@ -124,10 +169,18 @@ func natsOptions(c *Config) (nats.Options, error) {
124
169
125
170
// From src/code.cloudfoundry.org/gorouter/config/config.go
126
171
type Config struct {
172
+ Status StatusConfig `yaml:"status,omitempty"`
127
173
Nats NatsConfig `yaml:"nats,omitempty"`
128
174
NatsClientPingInterval time.Duration `yaml:"nats_client_ping_interval,omitempty"`
129
175
}
130
176
177
+ type StatusConfig struct {
178
+ Host string `yaml:"host"`
179
+ Port uint16 `yaml:"port"`
180
+ User string `yaml:"user"`
181
+ Pass string `yaml:"pass"`
182
+ }
183
+
131
184
type NatsConfig struct {
132
185
Hosts []NatsHost `yaml:"hosts"`
133
186
User string `yaml:"user"`
@@ -192,3 +245,131 @@ func (c *Config) NatsServers() []string {
192
245
193
246
return natsServers
194
247
}
248
+
249
+ func dumpRoutes (config * Config , filename string ) error {
250
+ res , err := http .Get (fmt .Sprintf ("http://%s:%s@%s:%d/routes" , config .Status .User , config .Status .Pass , config .Status .Host , config .Status .Port ))
251
+
252
+ if err != nil {
253
+ return err
254
+ }
255
+ if res .StatusCode != http .StatusOK {
256
+ return fmt .Errorf ("unexpected status code from /routes: %s" , res .Status )
257
+ }
258
+ file , err := os .Create (filename )
259
+ if err != nil {
260
+ return err
261
+ }
262
+ defer file .Close ()
263
+
264
+ var jsonObject map [string ]interface {}
265
+ dataIn , err := io .ReadAll (res .Body )
266
+ if err != nil {
267
+ return err
268
+ }
269
+ err = json .Unmarshal (dataIn , & jsonObject )
270
+ if err != nil {
271
+ return err
272
+ }
273
+ // Pretty print json so that humans can change it.
274
+ dataOut , err := json .MarshalIndent (jsonObject , "" , " " )
275
+ if err != nil {
276
+ return err
277
+ }
278
+ _ , err = file .Write (dataOut )
279
+ if err != nil {
280
+ return err
281
+ }
282
+
283
+ return err
284
+ }
285
+
286
+ // From src/code.cloudfoundry.org/gorouter/mbus/subscriber.go
287
+ type RegistryMessage struct {
288
+ Host string `json:"host"`
289
+ Port int `json:"port"`
290
+ Protocol string `json:"protocol"`
291
+ TLSPort int `json:"tls_port"`
292
+ Uris []string `json:"uris"`
293
+ Tags map [string ]string `json:"tags"`
294
+ App string `json:"app"`
295
+ StaleThresholdInSeconds int `json:"stale_threshold_in_seconds"`
296
+ RouteServiceURL string `json:"route_service_url"`
297
+ PrivateInstanceID string `json:"private_instance_id"`
298
+ ServerCertDomainSAN string `json:"server_cert_domain_san"`
299
+ PrivateInstanceIndex string `json:"private_instance_index"`
300
+ IsolationSegment string `json:"isolation_segment"`
301
+ EndpointUpdatedAtNs int64 `json:"endpoint_updated_at_ns"`
302
+ }
303
+
304
+ // From src/code.cloudfoundry.org/gorouter/route/pool.go
305
+ type RouteTableEntry struct {
306
+ Address string `json:"address"`
307
+ Protocol string `json:"protocol"`
308
+ TLS bool `json:"tls"`
309
+ TTL int `json:"ttl"`
310
+ RouteServiceUrl string `json:"route_service_url,omitempty"`
311
+ Tags map [string ]string `json:"tags"`
312
+ IsolationSegment string `json:"isolation_segment,omitempty"`
313
+ PrivateInstanceId string `json:"private_instance_id,omitempty"`
314
+ ServerCertDomainSAN string `json:"server_cert_domain_san,omitempty"`
315
+ }
316
+
317
+ func loadRoutes (config * Config , filename string ) error {
318
+ natsOptions , err := natsOptions (config )
319
+ if err != nil {
320
+ return err
321
+ }
322
+
323
+ natsConn , err := natsOptions .Connect ()
324
+ if err != nil {
325
+ return err
326
+ }
327
+
328
+ var routeTable map [string ][]RouteTableEntry
329
+
330
+ routesFile , err := os .Open (filename )
331
+ if err != nil {
332
+ return err
333
+ }
334
+ data , err := io .ReadAll (routesFile )
335
+ if err != nil {
336
+ return err
337
+ }
338
+ err = json .Unmarshal (data , & routeTable )
339
+ if err != nil {
340
+ return err
341
+ }
342
+ for uri , routes := range routeTable {
343
+ for _ , route := range routes {
344
+ host := strings .Split (route .Address , ":" )[0 ]
345
+ port , _ := strconv .Atoi (strings .Split (route .Address , ":" )[1 ])
346
+ tlsPort := 0
347
+ if route .TLS {
348
+ tlsPort = port
349
+ }
350
+
351
+ msg := RegistryMessage {
352
+ Host : host ,
353
+ Port : port ,
354
+ TLSPort : tlsPort ,
355
+ Protocol : route .Protocol ,
356
+ Uris : []string {uri },
357
+ Tags : route .Tags ,
358
+ App : route .Tags ["app_id" ],
359
+ StaleThresholdInSeconds : route .TTL ,
360
+ PrivateInstanceID : route .PrivateInstanceId ,
361
+ IsolationSegment : route .IsolationSegment ,
362
+ ServerCertDomainSAN : route .ServerCertDomainSAN ,
363
+ }
364
+ msgData , err := json .Marshal (msg )
365
+ if err != nil {
366
+ return err
367
+ }
368
+ err = natsConn .Publish ("router.register" , msgData )
369
+ if err != nil {
370
+ return err
371
+ }
372
+ }
373
+ }
374
+ return nil
375
+ }
0 commit comments