@@ -7,15 +7,30 @@ import (
7
7
"io"
8
8
"io/ioutil"
9
9
"net/http"
10
+ "reflect"
11
+ "strings"
10
12
)
11
13
12
- // APIVersion is a version of NGINX Plus API.
13
- const APIVersion = 5
14
+ const (
15
+ // APIVersion is a version of NGINX Plus API.
16
+ APIVersion = 5
14
17
15
- const pathNotFoundCode = "PathNotFound"
18
+ pathNotFoundCode = "PathNotFound"
19
+ streamContext = true
20
+ httpContext = false
21
+ defaultServerPort = "80"
22
+ )
16
23
17
- const streamContext = true
18
- const httpContext = false
24
+ // Default values for servers in Upstreams.
25
+ var (
26
+ defaultMaxConns = 0
27
+ defaultMaxFails = 1
28
+ defaultFailTimeout = "10s"
29
+ defaultSlowStart = "0s"
30
+ defaultBackup = false
31
+ defaultDown = false
32
+ defaultWeight = 1
33
+ )
19
34
20
35
// NginxClient lets you access NGINX Plus API.
21
36
type NginxClient struct {
@@ -29,13 +44,13 @@ type versions []int
29
44
type UpstreamServer struct {
30
45
ID int `json:"id,omitempty"`
31
46
Server string `json:"server"`
32
- MaxConns int `json:"max_conns"`
47
+ MaxConns * int `json:"max_conns,omitempty "`
33
48
MaxFails * int `json:"max_fails,omitempty"`
34
49
FailTimeout string `json:"fail_timeout,omitempty"`
35
50
SlowStart string `json:"slow_start,omitempty"`
36
- Route string `json:"route"`
37
- Backup bool `json:"backup"`
38
- Down bool `json:"down"`
51
+ Route string `json:"route,omitempty "`
52
+ Backup * bool `json:"backup,omitempty "`
53
+ Down * bool `json:"down,omitempty "`
39
54
Drain bool `json:"drain,omitempty"`
40
55
Weight * int `json:"weight,omitempty"`
41
56
Service string `json:"service,omitempty"`
@@ -45,12 +60,12 @@ type UpstreamServer struct {
45
60
type StreamUpstreamServer struct {
46
61
ID int `json:"id,omitempty"`
47
62
Server string `json:"server"`
48
- MaxConns int `json:"max_conns"`
63
+ MaxConns * int `json:"max_conns,omitempty "`
49
64
MaxFails * int `json:"max_fails,omitempty"`
50
65
FailTimeout string `json:"fail_timeout,omitempty"`
51
66
SlowStart string `json:"slow_start,omitempty"`
52
- Backup bool `json:"backup"`
53
- Down bool `json:"down"`
67
+ Backup * bool `json:"backup,omitempty "`
68
+ Down * bool `json:"down,omitempty "`
54
69
Weight * int `json:"weight,omitempty"`
55
70
Service string `json:"service,omitempty"`
56
71
}
@@ -469,32 +484,96 @@ func (client *NginxClient) DeleteHTTPServer(upstream string, server string) erro
469
484
// UpdateHTTPServers updates the servers of the upstream.
470
485
// Servers that are in the slice, but don't exist in NGINX will be added to NGINX.
471
486
// Servers that aren't in the slice, but exist in NGINX, will be removed from NGINX.
472
- func (client * NginxClient ) UpdateHTTPServers (upstream string , servers []UpstreamServer ) ([]UpstreamServer , []UpstreamServer , error ) {
487
+ // Servers that are in the slice and exist in NGINX, but have different parameters, will be updated.
488
+ func (client * NginxClient ) UpdateHTTPServers (upstream string , servers []UpstreamServer ) (added []UpstreamServer , deleted []UpstreamServer , updated []UpstreamServer , err error ) {
473
489
serversInNginx , err := client .GetHTTPServers (upstream )
474
490
if err != nil {
475
- return nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
491
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
492
+ }
493
+
494
+ // We assume port 80 if no port is set for servers.
495
+ var formattedServers []UpstreamServer
496
+ for _ , server := range servers {
497
+ server .Server = addPortToServer (server .Server )
498
+ formattedServers = append (formattedServers , server )
476
499
}
477
500
478
- toAdd , toDelete := determineUpdates (servers , serversInNginx )
501
+ toAdd , toDelete , toUpdate := determineUpdates (formattedServers , serversInNginx )
479
502
480
503
for _ , server := range toAdd {
481
504
err := client .AddHTTPServer (upstream , server )
482
505
if err != nil {
483
- return nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
506
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
484
507
}
485
508
}
486
509
487
510
for _ , server := range toDelete {
488
511
err := client .DeleteHTTPServer (upstream , server .Server )
489
512
if err != nil {
490
- return nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
513
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
491
514
}
492
515
}
493
516
494
- return toAdd , toDelete , nil
517
+ for _ , server := range toUpdate {
518
+ err := client .UpdateHTTPServer (upstream , server )
519
+ if err != nil {
520
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
521
+ }
522
+ }
523
+
524
+ return toAdd , toDelete , toUpdate , nil
495
525
}
496
526
497
- func determineUpdates (updatedServers []UpstreamServer , nginxServers []UpstreamServer ) (toAdd []UpstreamServer , toRemove []UpstreamServer ) {
527
+ // haveSameParameters checks if a given server has the same parameters as a server already present in NGINX. Order matters
528
+ func haveSameParameters (newServer UpstreamServer , serverNGX UpstreamServer ) bool {
529
+ newServer .ID = serverNGX .ID
530
+
531
+ if serverNGX .MaxConns != nil && newServer .MaxConns == nil {
532
+ newServer .MaxConns = & defaultMaxConns
533
+ }
534
+
535
+ if serverNGX .MaxFails != nil && newServer .MaxFails == nil {
536
+ newServer .MaxFails = & defaultMaxFails
537
+ }
538
+
539
+ if serverNGX .FailTimeout != "" && newServer .FailTimeout == "" {
540
+ newServer .FailTimeout = defaultFailTimeout
541
+ }
542
+
543
+ if serverNGX .SlowStart != "" && newServer .SlowStart == "" {
544
+ newServer .SlowStart = defaultSlowStart
545
+ }
546
+
547
+ if serverNGX .Backup != nil && newServer .Backup == nil {
548
+ newServer .Backup = & defaultBackup
549
+ }
550
+
551
+ if serverNGX .Down != nil && newServer .Down == nil {
552
+ newServer .Down = & defaultDown
553
+ }
554
+
555
+ if serverNGX .Weight != nil && newServer .Weight == nil {
556
+ newServer .Weight = & defaultWeight
557
+ }
558
+
559
+ return reflect .DeepEqual (newServer , serverNGX )
560
+ }
561
+
562
+ func determineUpdates (updatedServers []UpstreamServer , nginxServers []UpstreamServer ) (toAdd []UpstreamServer , toRemove []UpstreamServer , toUpdate []UpstreamServer ) {
563
+ for _ , server := range updatedServers {
564
+ updateFound := false
565
+ for _ , serverNGX := range nginxServers {
566
+ if server .Server == serverNGX .Server && ! haveSameParameters (server , serverNGX ) {
567
+ server .ID = serverNGX .ID
568
+ updateFound = true
569
+ break
570
+ }
571
+ }
572
+ if updateFound {
573
+ toUpdate = append (toUpdate , server )
574
+ }
575
+ }
576
+
498
577
for _ , server := range updatedServers {
499
578
found := false
500
579
for _ , serverNGX := range nginxServers {
@@ -608,7 +687,7 @@ func (client *NginxClient) delete(path string, expectedStatusCode int) error {
608
687
return nil
609
688
}
610
689
611
- func (client * NginxClient ) patch (path string , input interface {}) error {
690
+ func (client * NginxClient ) patch (path string , input interface {}, expectedStatusCode int ) error {
612
691
path = fmt .Sprintf ("%v/%v/%v/" , client .apiEndpoint , APIVersion , path )
613
692
614
693
jsonInput , err := json .Marshal (input )
@@ -627,10 +706,10 @@ func (client *NginxClient) patch(path string, input interface{}) error {
627
706
}
628
707
defer resp .Body .Close ()
629
708
630
- if resp .StatusCode != http . StatusNoContent {
709
+ if resp .StatusCode != expectedStatusCode {
631
710
return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
632
711
"failed to complete patch request: expected %v response, got %v" ,
633
- http . StatusNoContent , resp .StatusCode ))
712
+ expectedStatusCode , resp .StatusCode ))
634
713
}
635
714
return nil
636
715
}
@@ -692,29 +771,43 @@ func (client *NginxClient) DeleteStreamServer(upstream string, server string) er
692
771
// UpdateStreamServers updates the servers of the upstream.
693
772
// Servers that are in the slice, but don't exist in NGINX will be added to NGINX.
694
773
// Servers that aren't in the slice, but exist in NGINX, will be removed from NGINX.
695
- func (client * NginxClient ) UpdateStreamServers (upstream string , servers []StreamUpstreamServer ) ([]StreamUpstreamServer , []StreamUpstreamServer , error ) {
774
+ // Servers that are in the slice and exist in NGINX, but have different parameters, will be updated.
775
+ func (client * NginxClient ) UpdateStreamServers (upstream string , servers []StreamUpstreamServer ) (added []StreamUpstreamServer , deleted []StreamUpstreamServer , updated []StreamUpstreamServer , err error ) {
696
776
serversInNginx , err := client .GetStreamServers (upstream )
697
777
if err != nil {
698
- return nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
778
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
699
779
}
700
780
701
- toAdd , toDelete := determineStreamUpdates (servers , serversInNginx )
781
+ var formattedServers []StreamUpstreamServer
782
+ for _ , server := range servers {
783
+ server .Server = addPortToServer (server .Server )
784
+ formattedServers = append (formattedServers , server )
785
+ }
786
+
787
+ toAdd , toDelete , toUpdate := determineStreamUpdates (formattedServers , serversInNginx )
702
788
703
789
for _ , server := range toAdd {
704
790
err := client .AddStreamServer (upstream , server )
705
791
if err != nil {
706
- return nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
792
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
707
793
}
708
794
}
709
795
710
796
for _ , server := range toDelete {
711
797
err := client .DeleteStreamServer (upstream , server .Server )
712
798
if err != nil {
713
- return nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
799
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
800
+ }
801
+ }
802
+
803
+ for _ , server := range toUpdate {
804
+ err := client .UpdateStreamServer (upstream , server )
805
+ if err != nil {
806
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
714
807
}
715
808
}
716
809
717
- return toAdd , toDelete , nil
810
+ return toAdd , toDelete , toUpdate , nil
718
811
}
719
812
720
813
func (client * NginxClient ) getIDOfStreamServer (upstream string , name string ) (int , error ) {
@@ -732,7 +825,55 @@ func (client *NginxClient) getIDOfStreamServer(upstream string, name string) (in
732
825
return - 1 , nil
733
826
}
734
827
735
- func determineStreamUpdates (updatedServers []StreamUpstreamServer , nginxServers []StreamUpstreamServer ) (toAdd []StreamUpstreamServer , toRemove []StreamUpstreamServer ) {
828
+ // haveSameParametersForStream checks if a given server has the same parameters as a server already present in NGINX. Order matters
829
+ func haveSameParametersForStream (newServer StreamUpstreamServer , serverNGX StreamUpstreamServer ) bool {
830
+ newServer .ID = serverNGX .ID
831
+ if serverNGX .MaxConns != nil && newServer .MaxConns == nil {
832
+ newServer .MaxConns = & defaultMaxConns
833
+ }
834
+
835
+ if serverNGX .MaxFails != nil && newServer .MaxFails == nil {
836
+ newServer .MaxFails = & defaultMaxFails
837
+ }
838
+
839
+ if serverNGX .FailTimeout != "" && newServer .FailTimeout == "" {
840
+ newServer .FailTimeout = defaultFailTimeout
841
+ }
842
+
843
+ if serverNGX .SlowStart != "" && newServer .SlowStart == "" {
844
+ newServer .SlowStart = defaultSlowStart
845
+ }
846
+
847
+ if serverNGX .Backup != nil && newServer .Backup == nil {
848
+ newServer .Backup = & defaultBackup
849
+ }
850
+
851
+ if serverNGX .Down != nil && newServer .Down == nil {
852
+ newServer .Down = & defaultDown
853
+ }
854
+
855
+ if serverNGX .Weight != nil && newServer .Weight == nil {
856
+ newServer .Weight = & defaultWeight
857
+ }
858
+
859
+ return reflect .DeepEqual (newServer , serverNGX )
860
+ }
861
+
862
+ func determineStreamUpdates (updatedServers []StreamUpstreamServer , nginxServers []StreamUpstreamServer ) (toAdd []StreamUpstreamServer , toRemove []StreamUpstreamServer , toUpdate []StreamUpstreamServer ) {
863
+ for _ , server := range updatedServers {
864
+ updateFound := false
865
+ for _ , serverNGX := range nginxServers {
866
+ if server .Server == serverNGX .Server && ! haveSameParametersForStream (server , serverNGX ) {
867
+ server .ID = serverNGX .ID
868
+ updateFound = true
869
+ break
870
+ }
871
+ }
872
+ if updateFound {
873
+ toUpdate = append (toUpdate , server )
874
+ }
875
+ }
876
+
736
877
for _ , server := range updatedServers {
737
878
found := false
738
879
for _ , serverNGX := range nginxServers {
@@ -1059,7 +1200,7 @@ func (client *NginxClient) modifyKeyValPair(zone string, key string, val string,
1059
1200
1060
1201
path := fmt .Sprintf ("%v/keyvals/%v" , base , zone )
1061
1202
input := KeyValPairs {key : val }
1062
- err := client .patch (path , & input )
1203
+ err := client .patch (path , & input , http . StatusNoContent )
1063
1204
if err != nil {
1064
1205
return fmt .Errorf ("failed to update key value pair for %v/%v zone: %v" , base , zone , err )
1065
1206
}
@@ -1092,7 +1233,7 @@ func (client *NginxClient) deleteKeyValuePair(zone string, key string, stream bo
1092
1233
keyval [key ] = nil
1093
1234
1094
1235
path := fmt .Sprintf ("%v/keyvals/%v" , base , zone )
1095
- err := client .patch (path , & keyval )
1236
+ err := client .patch (path , & keyval , http . StatusNoContent )
1096
1237
if err != nil {
1097
1238
return fmt .Errorf ("failed to remove key values pair for %v/%v zone: %v" , base , zone , err )
1098
1239
}
@@ -1125,3 +1266,43 @@ func (client *NginxClient) deleteKeyValPairs(zone string, stream bool) error {
1125
1266
}
1126
1267
return nil
1127
1268
}
1269
+
1270
+ // UpdateHTTPServer updates the server of the upstream.
1271
+ func (client * NginxClient ) UpdateHTTPServer (upstream string , server UpstreamServer ) error {
1272
+ path := fmt .Sprintf ("http/upstreams/%v/servers/%v" , upstream , server .ID )
1273
+ server .ID = 0
1274
+ err := client .patch (path , & server , http .StatusOK )
1275
+ if err != nil {
1276
+ return fmt .Errorf ("failed to update %v server to %v upstream: %v" , server .Server , upstream , err )
1277
+ }
1278
+
1279
+ return nil
1280
+ }
1281
+
1282
+ // UpdateStreamServer updates the stream server of the upstream.
1283
+ func (client * NginxClient ) UpdateStreamServer (upstream string , server StreamUpstreamServer ) error {
1284
+ path := fmt .Sprintf ("stream/upstreams/%v/servers/%v" , upstream , server .ID )
1285
+ server .ID = 0
1286
+ err := client .patch (path , & server , http .StatusOK )
1287
+ if err != nil {
1288
+ return fmt .Errorf ("failed to update %v stream server to %v upstream: %v" , server .Server , upstream , err )
1289
+ }
1290
+
1291
+ return nil
1292
+ }
1293
+
1294
+ func addPortToServer (server string ) string {
1295
+ if len (strings .Split (server , ":" )) == 2 {
1296
+ return server
1297
+ }
1298
+
1299
+ if len (strings .Split (server , "]:" )) == 2 {
1300
+ return server
1301
+ }
1302
+
1303
+ if strings .HasPrefix (server , "unix:" ) {
1304
+ return server
1305
+ }
1306
+
1307
+ return fmt .Sprintf ("%v:%v" , server , defaultServerPort )
1308
+ }
0 commit comments