Skip to content

Commit 43df4f7

Browse files
authored
Add support for API version 6 (#108)
1 parent 7938737 commit 43df4f7

File tree

5 files changed

+163
-31
lines changed

5 files changed

+163
-31
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ This project includes a client library for working with NGINX Plus API.
1111

1212
## Compatibility
1313

14-
This Client works against version 5 of NGINX Plus API. Version 5 was introduced in NGINX Plus R19.
14+
This Client works against versions 4 to 6 of the NGINX Plus API. The table below shows the version of NGINX Plus where the API was first introduced.
15+
16+
| API version | NGINX Plus version |
17+
|-------------|--------------------|
18+
| 4 | R18 |
19+
| 5 | R19 |
20+
| 6 | R20 |
1521

1622
## Using the Client
1723

client/nginx.go

Lines changed: 121 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616

1717
const (
1818
// APIVersion is the default version of NGINX Plus API supported by the client.
19-
APIVersion = 5
19+
APIVersion = 6
2020

2121
pathNotFoundCode = "PathNotFound"
2222
streamContext = true
@@ -25,7 +25,7 @@ const (
2525
)
2626

2727
var (
28-
supportedAPIVersions = versions{4, 5}
28+
supportedAPIVersions = versions{4, 5, 6}
2929

3030
// Default values for servers in Upstreams.
3131
defaultMaxConns = 0
@@ -116,20 +116,23 @@ func (internalError *internalError) Wrap(err string) *internalError {
116116
// Stats represents NGINX Plus stats fetched from the NGINX Plus API.
117117
// https://nginx.org/en/docs/http/ngx_http_api_module.html
118118
type Stats struct {
119-
NginxInfo NginxInfo
120-
Caches Caches
121-
Processes Processes
122-
Connections Connections
123-
Slabs Slabs
124-
HTTPRequests HTTPRequests
125-
SSL SSL
126-
ServerZones ServerZones
127-
Upstreams Upstreams
128-
StreamServerZones StreamServerZones
129-
StreamUpstreams StreamUpstreams
130-
StreamZoneSync *StreamZoneSync
131-
LocationZones LocationZones
132-
Resolvers Resolvers
119+
NginxInfo NginxInfo
120+
Caches Caches
121+
Processes Processes
122+
Connections Connections
123+
Slabs Slabs
124+
HTTPRequests HTTPRequests
125+
SSL SSL
126+
ServerZones ServerZones
127+
Upstreams Upstreams
128+
StreamServerZones StreamServerZones
129+
StreamUpstreams StreamUpstreams
130+
StreamZoneSync *StreamZoneSync
131+
LocationZones LocationZones
132+
Resolvers Resolvers
133+
HTTPLimitRequests HTTPLimitRequests
134+
HTTPLimitConnections HTTPLimitConnections
135+
StreamLimitConnections StreamLimitConnections
133136
}
134137

135138
// NginxInfo contains general information about NGINX Plus.
@@ -418,6 +421,31 @@ type Processes struct {
418421
Respawned int64
419422
}
420423

424+
// HTTPLimitRequest represents HTTP Requests Rate Limiting
425+
type HTTPLimitRequest struct {
426+
Passed uint64
427+
Delayed uint64
428+
Rejected uint64
429+
DelayedDryRun uint64 `json:"delayed_dry_run"`
430+
RejectedDryRun uint64 `json:"rejected_dry_run"`
431+
}
432+
433+
// HTTPLimitRequests represents limit requests related stats
434+
type HTTPLimitRequests map[string]HTTPLimitRequest
435+
436+
// LimitConnection represents Connections Limiting
437+
type LimitConnection struct {
438+
Passed uint64
439+
Rejected uint64
440+
RejectedDryRun uint64 `json:"rejected_dry_run"`
441+
}
442+
443+
// HTTPLimitConnections represents limit connections related stats
444+
type HTTPLimitConnections map[string]LimitConnection
445+
446+
// StreamLimitConnections represents limit connections related stats
447+
type StreamLimitConnections map[string]LimitConnection
448+
421449
// NewNginxClient creates an NginxClient with the latest supported version.
422450
func NewNginxClient(httpClient *http.Client, apiEndpoint string) (*NginxClient, error) {
423451
return NewNginxClientWithVersion(httpClient, apiEndpoint, APIVersion)
@@ -1095,21 +1123,39 @@ func (client *NginxClient) GetStats() (*Stats, error) {
10951123
return nil, fmt.Errorf("failed to get stats: %w", err)
10961124
}
10971125

1126+
limitReqs, err := client.GetHTTPLimitReqs()
1127+
if err != nil {
1128+
return nil, fmt.Errorf("failed to get stats: %w", err)
1129+
}
1130+
1131+
limitConnsHTTP, err := client.GetHTTPConnectionsLimit()
1132+
if err != nil {
1133+
return nil, fmt.Errorf("failed to get stats: %w", err)
1134+
}
1135+
1136+
limitConnsStream, err := client.GetStreamConnectionsLimit()
1137+
if err != nil {
1138+
return nil, fmt.Errorf("failed to get stats: %w", err)
1139+
}
1140+
10981141
return &Stats{
1099-
NginxInfo: *info,
1100-
Caches: *caches,
1101-
Processes: *processes,
1102-
Slabs: *slabs,
1103-
Connections: *cons,
1104-
HTTPRequests: *requests,
1105-
SSL: *ssl,
1106-
ServerZones: *zones,
1107-
StreamServerZones: *streamZones,
1108-
Upstreams: *upstreams,
1109-
StreamUpstreams: *streamUpstreams,
1110-
StreamZoneSync: streamZoneSync,
1111-
LocationZones: *locationZones,
1112-
Resolvers: *resolvers,
1142+
NginxInfo: *info,
1143+
Caches: *caches,
1144+
Processes: *processes,
1145+
Slabs: *slabs,
1146+
Connections: *cons,
1147+
HTTPRequests: *requests,
1148+
SSL: *ssl,
1149+
ServerZones: *zones,
1150+
StreamServerZones: *streamZones,
1151+
Upstreams: *upstreams,
1152+
StreamUpstreams: *streamUpstreams,
1153+
StreamZoneSync: streamZoneSync,
1154+
LocationZones: *locationZones,
1155+
Resolvers: *resolvers,
1156+
HTTPLimitRequests: *limitReqs,
1157+
HTTPLimitConnections: *limitConnsHTTP,
1158+
StreamLimitConnections: *limitConnsStream,
11131159
}, nil
11141160
}
11151161

@@ -1500,3 +1546,48 @@ func addPortToServer(server string) string {
15001546

15011547
return fmt.Sprintf("%v:%v", server, defaultServerPort)
15021548
}
1549+
1550+
// GetHTTPLimitReqs returns http/limit_reqs stats.
1551+
func (client *NginxClient) GetHTTPLimitReqs() (*HTTPLimitRequests, error) {
1552+
var limitReqs HTTPLimitRequests
1553+
if client.version < 6 {
1554+
return &limitReqs, nil
1555+
}
1556+
err := client.get("http/limit_reqs", &limitReqs)
1557+
if err != nil {
1558+
return nil, fmt.Errorf("failed to get http limit requests: %w", err)
1559+
}
1560+
return &limitReqs, nil
1561+
}
1562+
1563+
// GetHTTPConnectionsLimit returns http/limit_conns stats.
1564+
func (client *NginxClient) GetHTTPConnectionsLimit() (*HTTPLimitConnections, error) {
1565+
var limitConns HTTPLimitConnections
1566+
if client.version < 6 {
1567+
return &limitConns, nil
1568+
}
1569+
err := client.get("http/limit_conns", &limitConns)
1570+
if err != nil {
1571+
return nil, fmt.Errorf("failed to get http connections limit: %w", err)
1572+
}
1573+
return &limitConns, nil
1574+
}
1575+
1576+
// GetStreamConnectionsLimit returns stream/limit_conns stats.
1577+
func (client *NginxClient) GetStreamConnectionsLimit() (*StreamLimitConnections, error) {
1578+
var limitConns StreamLimitConnections
1579+
if client.version < 6 {
1580+
return &limitConns, nil
1581+
}
1582+
err := client.get("stream/limit_conns", &limitConns)
1583+
if err != nil {
1584+
var ie *internalError
1585+
if errors.As(err, &ie) {
1586+
if ie.Code == pathNotFoundCode {
1587+
return &limitConns, nil
1588+
}
1589+
}
1590+
return nil, fmt.Errorf("failed to get stream connections limit: %w", err)
1591+
}
1592+
return &limitConns, nil
1593+
}

docker/nginx.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ stream {
3838
keyval_zone zone=zone_one_stream:32k;
3939
keyval $hostname $text zone=zone_one_stream;
4040
keyval_zone zone=zone_test_sync:32k timeout=5s sync;
41+
limit_conn_zone $binary_remote_addr zone=addr_stream:10m;
42+
43+
limit_conn addr_stream 1;
4144

4245
upstream stream_test {
4346
zone stream_test 64k;

docker/test.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ upstream test {
33
}
44

55
proxy_cache_path /var/cache/nginx keys_zone=http_cache:10m max_size=100m;
6+
limit_req_zone $binary_remote_addr zone=one:10m rate=1500r/s;
7+
limit_conn_zone $binary_remote_addr zone=addr:10m;
68

79
server {
810
listen 8080;
911

12+
limit_req zone=one burst=100;
13+
limit_conn addr 10;
14+
1015
location = /dashboard.html {
1116
root /usr/share/nginx/html;
1217
}

tests/client_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const (
1818
streamZoneSync = "zone_test_sync"
1919
locationZone = "location_test"
2020
resolverMetric = "resolver_test"
21+
reqZone = "one"
22+
connZone = "addr"
23+
streamConnZone = "addr_stream"
2124
)
2225

2326
var (
@@ -686,6 +689,22 @@ func TestStats(t *testing.T) {
686689
t.Errorf("Resolver %v not found", resolverMetric)
687690
}
688691

692+
if reqLimit, ok := stats.HTTPLimitRequests[reqZone]; ok {
693+
if reqLimit.Passed < 1 {
694+
t.Errorf("HTTP Reqs limit stats missing: %v", reqLimit.Passed)
695+
}
696+
} else {
697+
t.Errorf("HTTP Reqs limit %v not found", reqLimit)
698+
}
699+
700+
if connLimit, ok := stats.HTTPLimitConnections[connZone]; ok {
701+
if connLimit.Passed < 1 {
702+
t.Errorf("HTTP Limit connections stats missing: %v", connLimit.Passed)
703+
}
704+
} else {
705+
t.Errorf("HTTP Limit connections %v not found", connLimit)
706+
}
707+
689708
// cleanup upstream servers
690709
_, _, _, err = c.UpdateHTTPServers(upstream, []client.UpstreamServer{})
691710
if err != nil {
@@ -806,6 +825,14 @@ func TestStreamStats(t *testing.T) {
806825
t.Errorf("Stream upstream 'stream_test' not found")
807826
}
808827

828+
if streamConnLimit, ok := stats.StreamLimitConnections[streamConnZone]; ok {
829+
if streamConnLimit.Passed < 1 {
830+
t.Errorf("Stream Limit connections stats missing: %v", streamConnLimit.Passed)
831+
}
832+
} else {
833+
t.Errorf("Stream Limit connections %v not found", streamConnLimit)
834+
}
835+
809836
// cleanup stream upstream servers
810837
_, _, _, err = c.UpdateStreamServers(streamUpstream, []client.StreamUpstreamServer{})
811838
if err != nil {

0 commit comments

Comments
 (0)