@@ -12,6 +12,8 @@ import (
1212// APIVersion is a version of NGINX Plus API.
1313const APIVersion = 2
1414
15+ const streamNotConfiguredCode = "StreamNotConfigured"
16+
1517// NginxClient lets you access NGINX Plus API.
1618type NginxClient struct {
1719 apiEndpoint string
@@ -57,14 +59,33 @@ type apiError struct {
5759 Code string
5860}
5961
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+
6079// Stats represents NGINX Plus stats fetched from the NGINX Plus API.
6180// https://nginx.org/en/docs/http/ngx_http_api_module.html
6281type 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
6889}
6990
7091// Connections represents connection related stats.
@@ -101,7 +122,20 @@ type ServerZone struct {
101122 Sent uint64
102123}
103124
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.
105139type Responses struct {
106140 Responses1xx uint64 `json:"1xx"`
107141 Responses2xx uint64 `json:"2xx"`
@@ -111,6 +145,14 @@ type Responses struct {
111145 Total uint64
112146}
113147
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+
114156// Upstreams is a map of upstream stats by upstream name.
115157type Upstreams map [string ]Upstream
116158
@@ -123,6 +165,16 @@ type Upstream struct {
123165 Queue Queue
124166}
125167
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+
126178// Queue represents queue related stats for an upstream.
127179type Queue struct {
128180 Size int
@@ -155,6 +207,31 @@ type Peer struct {
155207 ResponseTime uint64 `json:"response_time"`
156208}
157209
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+
158235// HealthChecks represents health check related stats for a peer.
159236type HealthChecks struct {
160237 Checks uint64
@@ -214,13 +291,18 @@ func getAPIVersions(httpClient *http.Client, endpoint string) (*versions, error)
214291 return & vers , nil
215292}
216293
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 )
219296 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+ }
221300 }
222301
223- return fmt .Errorf ("%v; error: %v" , mainErr , apiErr .toString ())
302+ return & internalError {
303+ err : apiErrResp .toString (),
304+ apiError : apiErrResp .Error ,
305+ }
224306}
225307
226308func readAPIErrorResponse (respBody io.ReadCloser ) (* apiErrorResponse , error ) {
@@ -379,8 +461,9 @@ func (client *NginxClient) get(path string, data interface{}) error {
379461 }
380462 defer resp .Body .Close ()
381463 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 ))
384467 }
385468
386469 body , err := ioutil .ReadAll (resp .Body )
@@ -409,8 +492,9 @@ func (client *NginxClient) post(path string, input interface{}) error {
409492 }
410493 defer resp .Body .Close ()
411494 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 ))
414498 }
415499
416500 return nil
@@ -431,9 +515,9 @@ func (client *NginxClient) delete(path string) error {
431515 defer resp .Body .Close ()
432516
433517 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 ) )
437521 }
438522 return nil
439523}
@@ -458,7 +542,7 @@ func (client *NginxClient) GetStreamServers(upstream string) ([]StreamUpstreamSe
458542 return servers , nil
459543}
460544
461- // AddStreamServer adds the server to the upstream.
545+ // AddStreamServer adds the stream server to the upstream.
462546func (client * NginxClient ) AddStreamServer (upstream string , server StreamUpstreamServer ) error {
463547 id , err := client .getIDOfStreamServer (upstream , server .Server )
464548
@@ -572,7 +656,7 @@ func determineStreamUpdates(updatedServers []StreamUpstreamServer, nginxServers
572656 return
573657}
574658
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.
576660func (client * NginxClient ) GetStats () (* Stats , error ) {
577661 cons , err := client .getConnections ()
578662 if err != nil {
@@ -599,12 +683,24 @@ func (client *NginxClient) GetStats() (*Stats, error) {
599683 return nil , fmt .Errorf ("failed to get stats: %v" , err )
600684 }
601685
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+
602696 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 ,
608704 }, nil
609705}
610706
@@ -646,6 +742,20 @@ func (client *NginxClient) getServerZones() (*ServerZones, error) {
646742 return & zones , err
647743}
648744
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+
649759func (client * NginxClient ) getUpstreams () (* Upstreams , error ) {
650760 var upstreams Upstreams
651761 err := client .get ("http/upstreams" , & upstreams )
@@ -654,3 +764,17 @@ func (client *NginxClient) getUpstreams() (*Upstreams, error) {
654764 }
655765 return & upstreams , nil
656766}
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