Skip to content

Commit

Permalink
Add checks to see if stream endpoints exist before calling them (#174)
Browse files Browse the repository at this point in the history
* Add checks to see if stream endpoints exist before calling them

* Add unit tests

* Renamed functions

---------

Co-authored-by: Donal Hurley <[email protected]>
  • Loading branch information
dhurley and Donal Hurley authored Oct 6, 2023
1 parent cb52f11 commit df96e6b
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 19 deletions.
85 changes: 66 additions & 19 deletions client/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"reflect"
"strings"
"time"

"golang.org/x/exp/slices"
)

const (
Expand Down Expand Up @@ -1149,6 +1151,11 @@ func determineStreamUpdates(updatedServers []StreamUpstreamServer, nginxServers

// GetStats gets process, slab, connection, request, ssl, zone, stream zone, upstream and stream upstream related stats from the NGINX Plus API.
func (client *NginxClient) GetStats() (*Stats, error) {
endpoints, err := client.GetAvailableEndpoints()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

info, err := client.GetNginxInfo()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
Expand Down Expand Up @@ -1194,21 +1201,6 @@ func (client *NginxClient) GetStats() (*Stats, error) {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

streamZones, err := client.GetStreamServerZones()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

streamUpstreams, err := client.GetStreamUpstreams()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

streamZoneSync, err := client.GetStreamZoneSync()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

locationZones, err := client.GetLocationZones()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
Expand All @@ -1229,14 +1221,49 @@ func (client *NginxClient) GetStats() (*Stats, error) {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

limitConnsStream, err := client.GetStreamConnectionsLimit()
workers, err := client.GetWorkers()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

workers, err := client.GetWorkers()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
streamZones := &StreamServerZones{}
streamUpstreams := &StreamUpstreams{}
limitConnsStream := &StreamLimitConnections{}
streamZoneSync := &StreamZoneSync{}

if slices.Contains(endpoints, "stream") {
streamEndpoints, err := client.GetAvailableStreamEndpoints()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}

if slices.Contains(streamEndpoints, "server_zones") {
streamZones, err = client.GetStreamServerZones()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}
}

if slices.Contains(streamEndpoints, "upstreams") {
streamUpstreams, err = client.GetStreamUpstreams()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}
}

if slices.Contains(streamEndpoints, "limit_conns") {
limitConnsStream, err = client.GetStreamConnectionsLimit()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}
}

if slices.Contains(streamEndpoints, "zone_sync") {
streamZoneSync, err = client.GetStreamZoneSync()
if err != nil {
return nil, fmt.Errorf("failed to get stats: %w", err)
}
}
}

return &Stats{
Expand All @@ -1261,6 +1288,26 @@ func (client *NginxClient) GetStats() (*Stats, error) {
}, nil
}

// GetAvailableEndpoints returns available endpoints in the API.
func (client *NginxClient) GetAvailableEndpoints() ([]string, error) {
var endpoints []string
err := client.get("", &endpoints)
if err != nil {
return nil, fmt.Errorf("failed to get endpoints: %w", err)
}
return endpoints, nil
}

// GetAvailableStreamEndpoints returns available stream endpoints in the API.
func (client *NginxClient) GetAvailableStreamEndpoints() ([]string, error) {
var endpoints []string
err := client.get("stream", &endpoints)
if err != nil {
return nil, fmt.Errorf("failed to get endpoints: %w", err)
}
return endpoints, nil
}

// GetNginxInfo returns Nginx stats.
func (client *NginxClient) GetNginxInfo() (*NginxInfo, error) {
var info NginxInfo
Expand Down
52 changes: 52 additions & 0 deletions client/nginx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
)

Expand Down Expand Up @@ -589,3 +590,54 @@ func TestClientWithHTTPClient(t *testing.T) {
t.Fatalf("expected client to be nil, but got %v", client)
}
}

func TestGetStats_NoStreamEndpoint(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "/" {
_, err := w.Write([]byte(`[4, 5, 6, 7]`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
} else if r.RequestURI == "/7/" {
_, err := w.Write([]byte(`["nginx","processes","connections","slabs","http","resolvers","ssl"]`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
} else if strings.HasPrefix(r.RequestURI, "/7/stream") {
t.Fatal("Stream endpoint should not be called since it does not exist.")
} else {
_, err := w.Write([]byte(`{}`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
}))
defer ts.Close()

// Test creating a new client with a supported API version on the server
client, err := NewNginxClient(ts.URL, WithAPIVersion(7), WithCheckAPI())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if client == nil {
t.Fatalf("client is nil")
}

stats, err := client.GetStats()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if !reflect.DeepEqual(stats.StreamServerZones, StreamServerZones{}) {
t.Fatalf("StreamServerZones: expected %v, actual %v", StreamServerZones{}, stats.StreamServerZones)
}
if !reflect.DeepEqual(stats.StreamLimitConnections, StreamLimitConnections{}) {
t.Fatalf("StreamLimitConnections: expected %v, actual %v", StreamLimitConnections{}, stats.StreamLimitConnections)
}
if !reflect.DeepEqual(stats.StreamUpstreams, StreamUpstreams{}) {
t.Fatalf("StreamUpstreams: expected %v, actual %v", StreamUpstreams{}, stats.StreamUpstreams)
}
if !reflect.DeepEqual(stats.StreamZoneSync, &StreamZoneSync{}) {
t.Fatalf("StreamZoneSync: expected %v, actual %v", &StreamZoneSync{}, stats.StreamZoneSync)
}
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/nginxinc/nginx-plus-go-client

go 1.19

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

0 comments on commit df96e6b

Please sign in to comment.