@@ -12,6 +12,8 @@ import (
12
12
// APIVersion is a version of NGINX Plus API.
13
13
const APIVersion = 2
14
14
15
+ const streamNotConfiguredCode = "StreamNotConfigured"
16
+
15
17
// NginxClient lets you access NGINX Plus API.
16
18
type NginxClient struct {
17
19
apiEndpoint string
@@ -57,14 +59,33 @@ type apiError struct {
57
59
Code string
58
60
}
59
61
62
+ type internalError struct {
63
+ apiError
64
+ err string
65
+ }
66
+
67
+ // Error allows internalError to match the Error interface.
68
+ func (internalError * internalError ) Error () string {
69
+ return internalError .err
70
+ }
71
+
72
+ // Wrap is a way of including current context while preserving previous error information,
73
+ // similar to `return fmt.Errof("error doing foo, err: %v", err)` but for our internalError type.
74
+ func (internalError * internalError ) Wrap (err string ) * internalError {
75
+ internalError .err = fmt .Sprintf ("%v. %v" , err , internalError .err )
76
+ return internalError
77
+ }
78
+
60
79
// Stats represents NGINX Plus stats fetched from the NGINX Plus API.
61
80
// https://nginx.org/en/docs/http/ngx_http_api_module.html
62
81
type Stats struct {
63
- Connections Connections
64
- HTTPRequests HTTPRequests
65
- SSL SSL
66
- ServerZones ServerZones
67
- Upstreams Upstreams
82
+ Connections Connections
83
+ HTTPRequests HTTPRequests
84
+ SSL SSL
85
+ ServerZones ServerZones
86
+ Upstreams Upstreams
87
+ StreamServerZones StreamServerZones
88
+ StreamUpstreams StreamUpstreams
68
89
}
69
90
70
91
// Connections represents connection related stats.
@@ -101,7 +122,20 @@ type ServerZone struct {
101
122
Sent uint64
102
123
}
103
124
104
- // Responses represents HTTP reponse related stats.
125
+ // StreamServerZones is map of stream server zone stats by zone name.
126
+ type StreamServerZones map [string ]StreamServerZone
127
+
128
+ // StreamServerZone represents stream server zone related stats.
129
+ type StreamServerZone struct {
130
+ Processing uint64
131
+ Connections uint64
132
+ Sessions Sessions
133
+ Discarded uint64
134
+ Received uint64
135
+ Sent uint64
136
+ }
137
+
138
+ // Responses represents HTTP response related stats.
105
139
type Responses struct {
106
140
Responses1xx uint64 `json:"1xx"`
107
141
Responses2xx uint64 `json:"2xx"`
@@ -111,6 +145,14 @@ type Responses struct {
111
145
Total uint64
112
146
}
113
147
148
+ // Sessions represents stream session related stats.
149
+ type Sessions struct {
150
+ Sessions2xx uint64 `json:"2xx"`
151
+ Sessions4xx uint64 `josn:"4xx"`
152
+ Sessions5xx uint64 `josn:"5xx"`
153
+ Total uint64
154
+ }
155
+
114
156
// Upstreams is a map of upstream stats by upstream name.
115
157
type Upstreams map [string ]Upstream
116
158
@@ -123,6 +165,16 @@ type Upstream struct {
123
165
Queue Queue
124
166
}
125
167
168
+ // StreamUpstreams is a map of stream upstream stats by upstream name.
169
+ type StreamUpstreams map [string ]StreamUpstream
170
+
171
+ // StreamUpstream represents stream upstream related stats.
172
+ type StreamUpstream struct {
173
+ Peers []StreamPeer
174
+ Zombies int
175
+ Zone string
176
+ }
177
+
126
178
// Queue represents queue related stats for an upstream.
127
179
type Queue struct {
128
180
Size int
@@ -155,6 +207,31 @@ type Peer struct {
155
207
ResponseTime uint64 `json:"response_time"`
156
208
}
157
209
210
+ // StreamPeer represents peer (stream upstream server) related stats.
211
+ type StreamPeer struct {
212
+ ID int
213
+ Server string
214
+ Service string
215
+ Name string
216
+ Backup bool
217
+ Weight int
218
+ State string
219
+ Active uint64
220
+ MaxConns int `json:"max_conns"`
221
+ Connections uint64
222
+ ConnectTime int `json:"connect_time"`
223
+ FirstByteTime int `json:"first_byte_time"`
224
+ ResponseTime uint64 `json:"response_time"`
225
+ Sent uint64
226
+ Received uint64
227
+ Fails uint64
228
+ Unavail uint64
229
+ HealthChecks HealthChecks `json:"health_checks"`
230
+ Downtime uint64
231
+ Downstart string
232
+ Selected string
233
+ }
234
+
158
235
// HealthChecks represents health check related stats for a peer.
159
236
type HealthChecks struct {
160
237
Checks uint64
@@ -214,13 +291,18 @@ func getAPIVersions(httpClient *http.Client, endpoint string) (*versions, error)
214
291
return & vers , nil
215
292
}
216
293
217
- func createResponseMismatchError (respBody io.ReadCloser , mainErr error ) error {
218
- apiErr , err := readAPIErrorResponse (respBody )
294
+ func createResponseMismatchError (respBody io.ReadCloser ) * internalError {
295
+ apiErrResp , err := readAPIErrorResponse (respBody )
219
296
if err != nil {
220
- return fmt .Errorf ("%v; failed to read the response body: %v" , mainErr , err )
297
+ return & internalError {
298
+ err : fmt .Sprintf ("failed to read the response body: %v" , err ),
299
+ }
221
300
}
222
301
223
- return fmt .Errorf ("%v; error: %v" , mainErr , apiErr .toString ())
302
+ return & internalError {
303
+ err : apiErrResp .toString (),
304
+ apiError : apiErrResp .Error ,
305
+ }
224
306
}
225
307
226
308
func readAPIErrorResponse (respBody io.ReadCloser ) (* apiErrorResponse , error ) {
@@ -379,8 +461,9 @@ func (client *NginxClient) get(path string, data interface{}) error {
379
461
}
380
462
defer resp .Body .Close ()
381
463
if resp .StatusCode != http .StatusOK {
382
- mainErr := fmt .Errorf ("expected %v response, got %v" , http .StatusOK , resp .StatusCode )
383
- return createResponseMismatchError (resp .Body , mainErr )
464
+ return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
465
+ "expected %v response, got %v" ,
466
+ http .StatusOK , resp .StatusCode ))
384
467
}
385
468
386
469
body , err := ioutil .ReadAll (resp .Body )
@@ -409,8 +492,9 @@ func (client *NginxClient) post(path string, input interface{}) error {
409
492
}
410
493
defer resp .Body .Close ()
411
494
if resp .StatusCode != http .StatusCreated {
412
- mainErr := fmt .Errorf ("expected %v response, got %v" , http .StatusCreated , resp .StatusCode )
413
- return createResponseMismatchError (resp .Body , mainErr )
495
+ return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
496
+ "expected %v response, got %v" ,
497
+ http .StatusCreated , resp .StatusCode ))
414
498
}
415
499
416
500
return nil
@@ -431,9 +515,9 @@ func (client *NginxClient) delete(path string) error {
431
515
defer resp .Body .Close ()
432
516
433
517
if resp .StatusCode != http .StatusOK {
434
- mainErr := fmt .Errorf ( "failed to complete delete request: expected %v response, got %v" ,
435
- http . StatusOK , resp . StatusCode )
436
- return createResponseMismatchError ( resp . Body , mainErr )
518
+ return createResponseMismatchError ( resp . Body ). Wrap ( fmt .Sprintf (
519
+ "failed to complete delete request: expected %v response, got %v" ,
520
+ http . StatusOK , resp . StatusCode ) )
437
521
}
438
522
return nil
439
523
}
@@ -458,7 +542,7 @@ func (client *NginxClient) GetStreamServers(upstream string) ([]StreamUpstreamSe
458
542
return servers , nil
459
543
}
460
544
461
- // AddStreamServer adds the server to the upstream.
545
+ // AddStreamServer adds the stream server to the upstream.
462
546
func (client * NginxClient ) AddStreamServer (upstream string , server StreamUpstreamServer ) error {
463
547
id , err := client .getIDOfStreamServer (upstream , server .Server )
464
548
@@ -572,7 +656,7 @@ func determineStreamUpdates(updatedServers []StreamUpstreamServer, nginxServers
572
656
return
573
657
}
574
658
575
- // GetStats gets connection, request, ssl, zone, and upstream related stats from the NGINX Plus API.
659
+ // GetStats gets connection, request, ssl, zone, stream zone, upstream and stream upstream related stats from the NGINX Plus API.
576
660
func (client * NginxClient ) GetStats () (* Stats , error ) {
577
661
cons , err := client .getConnections ()
578
662
if err != nil {
@@ -599,12 +683,24 @@ func (client *NginxClient) GetStats() (*Stats, error) {
599
683
return nil , fmt .Errorf ("failed to get stats: %v" , err )
600
684
}
601
685
686
+ streamZones , err := client .getStreamServerZones ()
687
+ if err != nil {
688
+ return nil , fmt .Errorf ("failed to get stats: %v" , err )
689
+ }
690
+
691
+ streamUpstreams , err := client .getStreamUpstreams ()
692
+ if err != nil {
693
+ return nil , fmt .Errorf ("failed to get stats: %v" , err )
694
+ }
695
+
602
696
return & Stats {
603
- Connections : * cons ,
604
- HTTPRequests : * requests ,
605
- SSL : * ssl ,
606
- ServerZones : * zones ,
607
- Upstreams : * upstreams ,
697
+ Connections : * cons ,
698
+ HTTPRequests : * requests ,
699
+ SSL : * ssl ,
700
+ ServerZones : * zones ,
701
+ StreamServerZones : * streamZones ,
702
+ Upstreams : * upstreams ,
703
+ StreamUpstreams : * streamUpstreams ,
608
704
}, nil
609
705
}
610
706
@@ -646,6 +742,20 @@ func (client *NginxClient) getServerZones() (*ServerZones, error) {
646
742
return & zones , err
647
743
}
648
744
745
+ func (client * NginxClient ) getStreamServerZones () (* StreamServerZones , error ) {
746
+ var zones StreamServerZones
747
+ err := client .get ("stream/server_zones" , & zones )
748
+ if err != nil {
749
+ if err , ok := err .(* internalError ); ok {
750
+ if err .Code == streamNotConfiguredCode {
751
+ return & zones , nil
752
+ }
753
+ }
754
+ return nil , fmt .Errorf ("failed to get stream server zones: %v" , err )
755
+ }
756
+ return & zones , err
757
+ }
758
+
649
759
func (client * NginxClient ) getUpstreams () (* Upstreams , error ) {
650
760
var upstreams Upstreams
651
761
err := client .get ("http/upstreams" , & upstreams )
@@ -654,3 +764,17 @@ func (client *NginxClient) getUpstreams() (*Upstreams, error) {
654
764
}
655
765
return & upstreams , nil
656
766
}
767
+
768
+ func (client * NginxClient ) getStreamUpstreams () (* StreamUpstreams , error ) {
769
+ var upstreams StreamUpstreams
770
+ err := client .get ("stream/upstreams" , & upstreams )
771
+ if err != nil {
772
+ if err , ok := err .(* internalError ); ok {
773
+ if err .Code == streamNotConfiguredCode {
774
+ return & upstreams , nil
775
+ }
776
+ }
777
+ return nil , fmt .Errorf ("failed to get stream upstreams: %v" , err )
778
+ }
779
+ return & upstreams , nil
780
+ }
0 commit comments