Skip to content

Add zone sync endpoint to stats #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ docker-build:
docker build --build-arg NGINX_PLUS_VERSION=$(NGINX_PLUS_VERSION)~stretch -t $(NGINX_IMAGE) docker

run-nginx-plus:
docker run -d --name nginx-plus-test --rm -p 8080:8080 -p 8081:8081 $(NGINX_IMAGE)
docker network create --driver bridge test
docker run --network=test -d --name nginx-plus-test --network-alias=nginx-plus-test --rm -p 8080:8080 -p 8081:8081 $(NGINX_IMAGE)
docker run --network=test -d --name nginx-plus-test-helper --network-alias=nginx-plus-test --rm -p 8090:8080 -p 8091:8081 $(NGINX_IMAGE)

test-run:
go test client/*
Expand All @@ -26,4 +28,6 @@ test-run-no-stream-block:
go test tests/client_no_stream_test.go

clean:
docker kill nginx-plus-test
-docker kill nginx-plus-test
-docker kill nginx-plus-test-helper
-docker network rm test
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Run Tests:
$ make test
```

This will build and run an NGINX Plus container, execute the client tests against NGINX Plus API, and then clean up. If it fails and you want to clean up (i.e. stop the running container), please use `$ make clean`
This will build and run two NGINX Plus containers and create one docker network of type bridge, execute the client tests against both NGINX Plus APIs, and then clean up. If it fails and you want to clean up (i.e. stop the running containers and remove the docker network), please use `$ make clean`

## Support
This project is not covered by the NGINX Plus support contract.
44 changes: 44 additions & 0 deletions client/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type Stats struct {
Upstreams Upstreams
StreamServerZones StreamServerZones
StreamUpstreams StreamUpstreams
StreamZoneSync StreamZoneSync
}

// NginxInfo contains general information about NGINX Plus.
Expand Down Expand Up @@ -149,6 +150,27 @@ type StreamServerZone struct {
Sent uint64
}

// StreamZoneSync represents the sync information per each shared memory zone and the sync information per node in a cluster
type StreamZoneSync struct {
Zones map[string]SyncZone
Status StreamZoneSyncStatus
}

// SyncZone represents the syncronization status of a shared memory zone
type SyncZone struct {
RecordsPending uint64 `json:"records_pending"`
RecordsTotal uint64 `json:"records_total"`
}

// StreamZoneSyncStatus represents the status of a shared memory zone
type StreamZoneSyncStatus struct {
BytesIn uint64 `json:"bytes_in"`
MsgsIn uint64 `json:"msgs_in"`
MsgsOut uint64 `json:"msgs_out"`
BytesOut uint64 `json:"bytes_out"`
NodesOnline uint64 `json:"nodes_online"`
}

// Responses represents HTTP response related stats.
type Responses struct {
Responses1xx uint64 `json:"1xx"`
Expand Down Expand Up @@ -728,6 +750,11 @@ func (client *NginxClient) GetStats() (*Stats, error) {
return nil, fmt.Errorf("failed to get stats: %v", err)
}

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

return &Stats{
NginxInfo: *info,
Connections: *cons,
Expand All @@ -737,6 +764,7 @@ func (client *NginxClient) GetStats() (*Stats, error) {
StreamServerZones: *streamZones,
Upstreams: *upstreams,
StreamUpstreams: *streamUpstreams,
StreamZoneSync: *streamZoneSync,
}, nil
}

Expand Down Expand Up @@ -822,6 +850,22 @@ func (client *NginxClient) getStreamUpstreams() (*StreamUpstreams, error) {
return &upstreams, nil
}

func (client *NginxClient) getStreamZoneSync() (*StreamZoneSync, error) {
var streamZoneSync StreamZoneSync
err := client.get("stream/zone_sync", &streamZoneSync)
if err != nil {
if err, ok := err.(*internalError); ok {

if err.Code == pathNotFoundCode {
return &streamZoneSync, nil
}
}
return nil, fmt.Errorf("failed to get stream zone sync: %v", err)
}

return &streamZoneSync, err
}

// KeyValPairs are the key-value pairs stored in a zone.
type KeyValPairs map[string]string

Expand Down
9 changes: 9 additions & 0 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ http {
stream {
keyval_zone zone=zone_one_stream:32k;
keyval $hostname $text zone=zone_one_stream;
keyval_zone zone=zone_test_sync:32k timeout=5s sync;

upstream stream_test {
zone stream_test 64k;
Expand All @@ -49,4 +50,12 @@ stream {
health_check interval=10 fails=3 passes=1;
}

resolver 127.0.0.11 valid=5s;

server {
listen 7777;

zone_sync;
zone_sync_server nginx-plus-test:7777 resolve;
}
}
96 changes: 95 additions & 1 deletion tests/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
const (
upstream = "test"
streamUpstream = "stream_test"
streamZoneSync = "zone_test_sync"
)

func TestStreamClient(t *testing.T) {
Expand Down Expand Up @@ -673,7 +674,8 @@ func TestKeyValueStream(t *testing.T) {
t.Errorf("Couldn't get keyvals, %v", err)
}
expectedKeyValuePairsByZone := client.KeyValPairsByZone{
zoneName: expectedKeyValPairs,
zoneName: expectedKeyValPairs,
streamZoneSync: client.KeyValPairs{},
}
if !reflect.DeepEqual(expectedKeyValuePairsByZone, keyValPairsByZone) {
t.Errorf("maps are not equal. expected: %+v, got: %+v", expectedKeyValuePairsByZone, keyValPairsByZone)
Expand Down Expand Up @@ -741,6 +743,98 @@ func TestKeyValueStream(t *testing.T) {
}
}

func TestStreamZoneSync(t *testing.T) {
c1, err := client.NewNginxClient(&http.Client{}, "http://127.0.0.1:8080/api")
if err != nil {
t.Fatalf("Error connecting to nginx: %v", err)
}

c2, err := client.NewNginxClient(&http.Client{}, "http://127.0.0.1:8090/api")
if err != nil {
t.Fatalf("Error connecting to nginx: %v", err)
}

err = c1.AddStreamKeyValPair(streamZoneSync, "key1", "val1")
if err != nil {
t.Errorf("Couldn't set keyvals: %v", err)
}

// wait for nodes to sync information of synced zones
time.Sleep(5 * time.Second)

statsC1, err := c1.GetStats()
if err != nil {
t.Errorf("Error getting stats: %v", err)
}

if statsC1.StreamZoneSync.Status.NodesOnline == 0 {
t.Errorf("At least 1 node must be online")
}

if statsC1.StreamZoneSync.Status.MsgsOut == 0 {
t.Errorf("Msgs out cannot be 0")
}

if statsC1.StreamZoneSync.Status.MsgsIn == 0 {
t.Errorf("Msgs in cannot be 0")
}

if statsC1.StreamZoneSync.Status.BytesIn == 0 {
t.Errorf("Bytes in cannot be 0")
}

if statsC1.StreamZoneSync.Status.BytesOut == 0 {
t.Errorf("Bytes Out cannot be 0")
}

if zone, ok := statsC1.StreamZoneSync.Zones[streamZoneSync]; ok {
if zone.RecordsTotal == 0 {
t.Errorf("Total records cannot be 0 after adding keyvals")
}
if zone.RecordsPending != 0 {
t.Errorf("Pending records must be 0 after adding keyvals")
}
} else {
t.Errorf("Sync zone %v missing in stats", streamZoneSync)
}

statsC2, err := c2.GetStats()
if err != nil {
t.Errorf("Error getting stats: %v", err)
}

if statsC2.StreamZoneSync.Status.NodesOnline == 0 {
t.Errorf("At least 1 node must be online")
}

if statsC2.StreamZoneSync.Status.MsgsOut != 0 {
t.Errorf("Msgs out must be 0")
}

if statsC2.StreamZoneSync.Status.MsgsIn == 0 {
t.Errorf("Msgs in cannot be 0")
}

if statsC2.StreamZoneSync.Status.BytesIn == 0 {
t.Errorf("Bytes in cannot be 0")
}

if statsC2.StreamZoneSync.Status.BytesOut != 0 {
t.Errorf("Bytes out must be 0")
}

if zone, ok := statsC2.StreamZoneSync.Zones[streamZoneSync]; ok {
if zone.RecordsTotal == 0 {
t.Errorf("Total records cannot be 0 after adding keyvals")
}
if zone.RecordsPending != 0 {
t.Errorf("Pending records must be 0 after adding keyvals")
}
} else {
t.Errorf("Sync zone %v missing in stats", streamZoneSync)
}
}

func compareUpstreamServers(x []client.UpstreamServer, y []client.UpstreamServer) bool {
var xServers []string
for _, us := range x {
Expand Down