From 010207bc4fa2d27130a7bc6e2319321de8fe7b05 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Thu, 5 Oct 2023 16:34:29 +0100 Subject: [PATCH 1/3] Add checks to see if stream endpoints exist before calling them --- client/nginx.go | 85 ++++++++++++++++++++++++++++++++++++++----------- go.mod | 2 ++ go.sum | 2 ++ 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/client/nginx.go b/client/nginx.go index 206ecf8f..a8c31ad6 100644 --- a/client/nginx.go +++ b/client/nginx.go @@ -11,6 +11,8 @@ import ( "reflect" "strings" "time" + + "golang.org/x/exp/slices" ) const ( @@ -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.GetAvailableEndpoint() + 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) @@ -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) @@ -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.GetAvailableStreamEndpoint() + 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{ @@ -1261,6 +1288,26 @@ func (client *NginxClient) GetStats() (*Stats, error) { }, nil } +// GetAvailableEndpoint returns available endpoints in the API. +func (client *NginxClient) GetAvailableEndpoint() ([]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 +} + +// GetAvailableStreamEndpoint returns available stream endpoints in the API. +func (client *NginxClient) GetAvailableStreamEndpoint() ([]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 diff --git a/go.mod b/go.mod index 8916c37c..06f94a0c 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index e69de29b..fa6da3f7 100644 --- a/go.sum +++ b/go.sum @@ -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= From 6a14655c7f2f2fd2974965246dab8492bb60bbc0 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Fri, 6 Oct 2023 10:46:13 +0100 Subject: [PATCH 2/3] Add unit tests --- client/nginx_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/client/nginx_test.go b/client/nginx_test.go index 0acf9914..3465f6a2 100644 --- a/client/nginx_test.go +++ b/client/nginx_test.go @@ -4,6 +4,7 @@ import ( "net/http" "net/http/httptest" "reflect" + "strings" "testing" ) @@ -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) + } +} From 8e7def6c93617b1edbf26db3d74655aef64d6fd0 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Fri, 6 Oct 2023 13:32:09 +0100 Subject: [PATCH 3/3] Renamed functions --- client/nginx.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/nginx.go b/client/nginx.go index a8c31ad6..60798f83 100644 --- a/client/nginx.go +++ b/client/nginx.go @@ -1151,7 +1151,7 @@ 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.GetAvailableEndpoint() + endpoints, err := client.GetAvailableEndpoints() if err != nil { return nil, fmt.Errorf("failed to get stats: %w", err) } @@ -1232,7 +1232,7 @@ func (client *NginxClient) GetStats() (*Stats, error) { streamZoneSync := &StreamZoneSync{} if slices.Contains(endpoints, "stream") { - streamEndpoints, err := client.GetAvailableStreamEndpoint() + streamEndpoints, err := client.GetAvailableStreamEndpoints() if err != nil { return nil, fmt.Errorf("failed to get stats: %w", err) } @@ -1288,8 +1288,8 @@ func (client *NginxClient) GetStats() (*Stats, error) { }, nil } -// GetAvailableEndpoint returns available endpoints in the API. -func (client *NginxClient) GetAvailableEndpoint() ([]string, error) { +// GetAvailableEndpoints returns available endpoints in the API. +func (client *NginxClient) GetAvailableEndpoints() ([]string, error) { var endpoints []string err := client.get("", &endpoints) if err != nil { @@ -1298,8 +1298,8 @@ func (client *NginxClient) GetAvailableEndpoint() ([]string, error) { return endpoints, nil } -// GetAvailableStreamEndpoint returns available stream endpoints in the API. -func (client *NginxClient) GetAvailableStreamEndpoint() ([]string, error) { +// 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 {