Skip to content

Commit df96e6b

Browse files
authored
Add checks to see if stream endpoints exist before calling them (#174)
* Add checks to see if stream endpoints exist before calling them * Add unit tests * Renamed functions --------- Co-authored-by: Donal Hurley <d.hurley@f5.com>
1 parent cb52f11 commit df96e6b

File tree

4 files changed

+122
-19
lines changed

4 files changed

+122
-19
lines changed

client/nginx.go

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"reflect"
1212
"strings"
1313
"time"
14+
15+
"golang.org/x/exp/slices"
1416
)
1517

1618
const (
@@ -1149,6 +1151,11 @@ func determineStreamUpdates(updatedServers []StreamUpstreamServer, nginxServers
11491151

11501152
// GetStats gets process, slab, connection, request, ssl, zone, stream zone, upstream and stream upstream related stats from the NGINX Plus API.
11511153
func (client *NginxClient) GetStats() (*Stats, error) {
1154+
endpoints, err := client.GetAvailableEndpoints()
1155+
if err != nil {
1156+
return nil, fmt.Errorf("failed to get stats: %w", err)
1157+
}
1158+
11521159
info, err := client.GetNginxInfo()
11531160
if err != nil {
11541161
return nil, fmt.Errorf("failed to get stats: %w", err)
@@ -1194,21 +1201,6 @@ func (client *NginxClient) GetStats() (*Stats, error) {
11941201
return nil, fmt.Errorf("failed to get stats: %w", err)
11951202
}
11961203

1197-
streamZones, err := client.GetStreamServerZones()
1198-
if err != nil {
1199-
return nil, fmt.Errorf("failed to get stats: %w", err)
1200-
}
1201-
1202-
streamUpstreams, err := client.GetStreamUpstreams()
1203-
if err != nil {
1204-
return nil, fmt.Errorf("failed to get stats: %w", err)
1205-
}
1206-
1207-
streamZoneSync, err := client.GetStreamZoneSync()
1208-
if err != nil {
1209-
return nil, fmt.Errorf("failed to get stats: %w", err)
1210-
}
1211-
12121204
locationZones, err := client.GetLocationZones()
12131205
if err != nil {
12141206
return nil, fmt.Errorf("failed to get stats: %w", err)
@@ -1229,14 +1221,49 @@ func (client *NginxClient) GetStats() (*Stats, error) {
12291221
return nil, fmt.Errorf("failed to get stats: %w", err)
12301222
}
12311223

1232-
limitConnsStream, err := client.GetStreamConnectionsLimit()
1224+
workers, err := client.GetWorkers()
12331225
if err != nil {
12341226
return nil, fmt.Errorf("failed to get stats: %w", err)
12351227
}
12361228

1237-
workers, err := client.GetWorkers()
1238-
if err != nil {
1239-
return nil, fmt.Errorf("failed to get stats: %w", err)
1229+
streamZones := &StreamServerZones{}
1230+
streamUpstreams := &StreamUpstreams{}
1231+
limitConnsStream := &StreamLimitConnections{}
1232+
streamZoneSync := &StreamZoneSync{}
1233+
1234+
if slices.Contains(endpoints, "stream") {
1235+
streamEndpoints, err := client.GetAvailableStreamEndpoints()
1236+
if err != nil {
1237+
return nil, fmt.Errorf("failed to get stats: %w", err)
1238+
}
1239+
1240+
if slices.Contains(streamEndpoints, "server_zones") {
1241+
streamZones, err = client.GetStreamServerZones()
1242+
if err != nil {
1243+
return nil, fmt.Errorf("failed to get stats: %w", err)
1244+
}
1245+
}
1246+
1247+
if slices.Contains(streamEndpoints, "upstreams") {
1248+
streamUpstreams, err = client.GetStreamUpstreams()
1249+
if err != nil {
1250+
return nil, fmt.Errorf("failed to get stats: %w", err)
1251+
}
1252+
}
1253+
1254+
if slices.Contains(streamEndpoints, "limit_conns") {
1255+
limitConnsStream, err = client.GetStreamConnectionsLimit()
1256+
if err != nil {
1257+
return nil, fmt.Errorf("failed to get stats: %w", err)
1258+
}
1259+
}
1260+
1261+
if slices.Contains(streamEndpoints, "zone_sync") {
1262+
streamZoneSync, err = client.GetStreamZoneSync()
1263+
if err != nil {
1264+
return nil, fmt.Errorf("failed to get stats: %w", err)
1265+
}
1266+
}
12401267
}
12411268

12421269
return &Stats{
@@ -1261,6 +1288,26 @@ func (client *NginxClient) GetStats() (*Stats, error) {
12611288
}, nil
12621289
}
12631290

1291+
// GetAvailableEndpoints returns available endpoints in the API.
1292+
func (client *NginxClient) GetAvailableEndpoints() ([]string, error) {
1293+
var endpoints []string
1294+
err := client.get("", &endpoints)
1295+
if err != nil {
1296+
return nil, fmt.Errorf("failed to get endpoints: %w", err)
1297+
}
1298+
return endpoints, nil
1299+
}
1300+
1301+
// GetAvailableStreamEndpoints returns available stream endpoints in the API.
1302+
func (client *NginxClient) GetAvailableStreamEndpoints() ([]string, error) {
1303+
var endpoints []string
1304+
err := client.get("stream", &endpoints)
1305+
if err != nil {
1306+
return nil, fmt.Errorf("failed to get endpoints: %w", err)
1307+
}
1308+
return endpoints, nil
1309+
}
1310+
12641311
// GetNginxInfo returns Nginx stats.
12651312
func (client *NginxClient) GetNginxInfo() (*NginxInfo, error) {
12661313
var info NginxInfo

client/nginx_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"net/http"
55
"net/http/httptest"
66
"reflect"
7+
"strings"
78
"testing"
89
)
910

@@ -589,3 +590,54 @@ func TestClientWithHTTPClient(t *testing.T) {
589590
t.Fatalf("expected client to be nil, but got %v", client)
590591
}
591592
}
593+
594+
func TestGetStats_NoStreamEndpoint(t *testing.T) {
595+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
596+
if r.RequestURI == "/" {
597+
_, err := w.Write([]byte(`[4, 5, 6, 7]`))
598+
if err != nil {
599+
t.Fatalf("unexpected error: %v", err)
600+
}
601+
} else if r.RequestURI == "/7/" {
602+
_, err := w.Write([]byte(`["nginx","processes","connections","slabs","http","resolvers","ssl"]`))
603+
if err != nil {
604+
t.Fatalf("unexpected error: %v", err)
605+
}
606+
} else if strings.HasPrefix(r.RequestURI, "/7/stream") {
607+
t.Fatal("Stream endpoint should not be called since it does not exist.")
608+
} else {
609+
_, err := w.Write([]byte(`{}`))
610+
if err != nil {
611+
t.Fatalf("unexpected error: %v", err)
612+
}
613+
}
614+
}))
615+
defer ts.Close()
616+
617+
// Test creating a new client with a supported API version on the server
618+
client, err := NewNginxClient(ts.URL, WithAPIVersion(7), WithCheckAPI())
619+
if err != nil {
620+
t.Fatalf("unexpected error: %v", err)
621+
}
622+
if client == nil {
623+
t.Fatalf("client is nil")
624+
}
625+
626+
stats, err := client.GetStats()
627+
if err != nil {
628+
t.Fatalf("unexpected error: %v", err)
629+
}
630+
631+
if !reflect.DeepEqual(stats.StreamServerZones, StreamServerZones{}) {
632+
t.Fatalf("StreamServerZones: expected %v, actual %v", StreamServerZones{}, stats.StreamServerZones)
633+
}
634+
if !reflect.DeepEqual(stats.StreamLimitConnections, StreamLimitConnections{}) {
635+
t.Fatalf("StreamLimitConnections: expected %v, actual %v", StreamLimitConnections{}, stats.StreamLimitConnections)
636+
}
637+
if !reflect.DeepEqual(stats.StreamUpstreams, StreamUpstreams{}) {
638+
t.Fatalf("StreamUpstreams: expected %v, actual %v", StreamUpstreams{}, stats.StreamUpstreams)
639+
}
640+
if !reflect.DeepEqual(stats.StreamZoneSync, &StreamZoneSync{}) {
641+
t.Fatalf("StreamZoneSync: expected %v, actual %v", &StreamZoneSync{}, stats.StreamZoneSync)
642+
}
643+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/nginxinc/nginx-plus-go-client
22

33
go 1.19
4+
5+
require golang.org/x/exp v0.0.0-20230905200255-921286631fa9

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
2+
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=

0 commit comments

Comments
 (0)