@@ -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,93 @@ 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
+ for _ , server := range servers {
496
+ server .Server = addPortToServer (server .Server )
476
497
}
477
498
478
- toAdd , toDelete := determineUpdates (servers , serversInNginx )
499
+ toAdd , toDelete , toUpdate := determineUpdates (servers , serversInNginx )
479
500
480
501
for _ , server := range toAdd {
481
502
err := client .AddHTTPServer (upstream , server )
482
503
if err != nil {
483
- return nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
504
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
484
505
}
485
506
}
486
507
487
508
for _ , server := range toDelete {
488
509
err := client .DeleteHTTPServer (upstream , server .Server )
489
510
if err != nil {
490
- return nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
511
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
491
512
}
492
513
}
493
514
494
- return toAdd , toDelete , nil
515
+ for _ , server := range toUpdate {
516
+ err := client .UpdateHTTPServer (upstream , server )
517
+ if err != nil {
518
+ return nil , nil , nil , fmt .Errorf ("failed to update servers of %v upstream: %v" , upstream , err )
519
+ }
520
+ }
521
+
522
+ return toAdd , toDelete , toUpdate , nil
495
523
}
496
524
497
- func determineUpdates (updatedServers []UpstreamServer , nginxServers []UpstreamServer ) (toAdd []UpstreamServer , toRemove []UpstreamServer ) {
525
+ // haveSameParameters checks if a given server has the same parameters an NGINX server already present. Order matters
526
+ func haveSameParameters (newServer UpstreamServer , serverNGX UpstreamServer ) bool {
527
+ newServer .ID = serverNGX .ID
528
+
529
+ if serverNGX .MaxConns != nil && newServer .MaxConns == nil {
530
+ newServer .MaxConns = & defaultMaxConns
531
+ }
532
+
533
+ if serverNGX .MaxFails != nil && newServer .MaxFails == nil {
534
+ newServer .MaxFails = & defaultMaxFails
535
+ }
536
+
537
+ if serverNGX .FailTimeout != "" && newServer .FailTimeout == "" {
538
+ newServer .FailTimeout = defaultFailTimeout
539
+ }
540
+
541
+ if serverNGX .SlowStart != "" && newServer .SlowStart == "" {
542
+ newServer .SlowStart = defaultSlowStart
543
+ }
544
+
545
+ if serverNGX .Backup != nil && newServer .Backup == nil {
546
+ newServer .Backup = & defaultBackup
547
+ }
548
+
549
+ if serverNGX .Down != nil && newServer .Down == nil {
550
+ newServer .Down = & defaultDown
551
+ }
552
+
553
+ if serverNGX .Weight != nil && newServer .Weight == nil {
554
+ newServer .Weight = & defaultWeight
555
+ }
556
+
557
+ return reflect .DeepEqual (newServer , serverNGX )
558
+ }
559
+
560
+ func determineUpdates (updatedServers []UpstreamServer , nginxServers []UpstreamServer ) (toAdd []UpstreamServer , toRemove []UpstreamServer , toUpdate []UpstreamServer ) {
561
+ for _ , server := range updatedServers {
562
+ updateFound := false
563
+ for _ , serverNGX := range nginxServers {
564
+ if server .Server == serverNGX .Server && ! haveSameParameters (server , serverNGX ) {
565
+ updateFound = true
566
+ break
567
+ }
568
+ }
569
+ if updateFound {
570
+ toUpdate = append (toUpdate , server )
571
+ }
572
+ }
573
+
498
574
for _ , server := range updatedServers {
499
575
found := false
500
576
for _ , serverNGX := range nginxServers {
@@ -608,7 +684,7 @@ func (client *NginxClient) delete(path string, expectedStatusCode int) error {
608
684
return nil
609
685
}
610
686
611
- func (client * NginxClient ) patch (path string , input interface {}) error {
687
+ func (client * NginxClient ) patch (path string , input interface {}, expectedStatusCode int ) error {
612
688
path = fmt .Sprintf ("%v/%v/%v/" , client .apiEndpoint , APIVersion , path )
613
689
614
690
jsonInput , err := json .Marshal (input )
@@ -627,10 +703,10 @@ func (client *NginxClient) patch(path string, input interface{}) error {
627
703
}
628
704
defer resp .Body .Close ()
629
705
630
- if resp .StatusCode != http . StatusNoContent {
706
+ if resp .StatusCode != expectedStatusCode {
631
707
return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
632
708
"failed to complete patch request: expected %v response, got %v" ,
633
- http . StatusNoContent , resp .StatusCode ))
709
+ expectedStatusCode , resp .StatusCode ))
634
710
}
635
711
return nil
636
712
}
@@ -655,6 +731,7 @@ func (client *NginxClient) GetStreamServers(upstream string) ([]StreamUpstreamSe
655
731
656
732
// AddStreamServer adds the stream server to the upstream.
657
733
func (client * NginxClient ) AddStreamServer (upstream string , server StreamUpstreamServer ) error {
734
+ server .Server = addPortToServer (server .Server )
658
735
id , err := client .getIDOfStreamServer (upstream , server .Server )
659
736
if err != nil {
660
737
return fmt .Errorf ("failed to add %v stream server to %v upstream: %v" , server .Server , upstream , err )
@@ -673,6 +750,7 @@ func (client *NginxClient) AddStreamServer(upstream string, server StreamUpstrea
673
750
674
751
// DeleteStreamServer the server from the upstream.
675
752
func (client * NginxClient ) DeleteStreamServer (upstream string , server string ) error {
753
+ server = addPortToServer (server )
676
754
id , err := client .getIDOfStreamServer (upstream , server )
677
755
if err != nil {
678
756
return fmt .Errorf ("failed to remove %v stream server from %v upstream: %v" , server , upstream , err )
@@ -692,29 +770,42 @@ func (client *NginxClient) DeleteStreamServer(upstream string, server string) er
692
770
// UpdateStreamServers updates the servers of the upstream.
693
771
// Servers that are in the slice, but don't exist in NGINX will be added to NGINX.
694
772
// 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 ) {
773
+ // Servers that are in the slice and exist in NGINX, but have different parameters, will be updated.
774
+ func (client * NginxClient ) UpdateStreamServers (upstream string , servers []StreamUpstreamServer ) (added []StreamUpstreamServer , deleted []StreamUpstreamServer , updated []StreamUpstreamServer , err error ) {
696
775
serversInNginx , err := client .GetStreamServers (upstream )
697
776
if err != nil {
698
- return nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
777
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
699
778
}
700
779
701
- toAdd , toDelete := determineStreamUpdates (servers , serversInNginx )
780
+ // We assume port 80 if no port is set for servers.
781
+ for _ , server := range servers {
782
+ server .Server = addPortToServer (server .Server )
783
+ }
784
+
785
+ toAdd , toDelete , toUpdate := determineStreamUpdates (servers , serversInNginx )
702
786
703
787
for _ , server := range toAdd {
704
788
err := client .AddStreamServer (upstream , server )
705
789
if err != nil {
706
- return nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
790
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
707
791
}
708
792
}
709
793
710
794
for _ , server := range toDelete {
711
795
err := client .DeleteStreamServer (upstream , server .Server )
712
796
if err != nil {
713
- return nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
797
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
798
+ }
799
+ }
800
+
801
+ for _ , server := range toUpdate {
802
+ err := client .UpdateStreamServer (upstream , server )
803
+ if err != nil {
804
+ return nil , nil , nil , fmt .Errorf ("failed to update stream servers of %v upstream: %v" , upstream , err )
714
805
}
715
806
}
716
807
717
- return toAdd , toDelete , nil
808
+ return toAdd , toDelete , toUpdate , nil
718
809
}
719
810
720
811
func (client * NginxClient ) getIDOfStreamServer (upstream string , name string ) (int , error ) {
@@ -732,7 +823,54 @@ func (client *NginxClient) getIDOfStreamServer(upstream string, name string) (in
732
823
return - 1 , nil
733
824
}
734
825
735
- func determineStreamUpdates (updatedServers []StreamUpstreamServer , nginxServers []StreamUpstreamServer ) (toAdd []StreamUpstreamServer , toRemove []StreamUpstreamServer ) {
826
+ // haveStreamSameParameters checks if a given stream server has the same parameters an NGINX server already present. Order matters
827
+ func haveStreamSameParameters (newServer StreamUpstreamServer , serverNGX StreamUpstreamServer ) bool {
828
+ newServer .ID = serverNGX .ID
829
+ if serverNGX .MaxConns != nil && newServer .MaxConns == nil {
830
+ newServer .MaxConns = & defaultMaxConns
831
+ }
832
+
833
+ if serverNGX .MaxFails != nil && newServer .MaxFails == nil {
834
+ newServer .MaxFails = & defaultMaxFails
835
+ }
836
+
837
+ if serverNGX .FailTimeout != "" && newServer .FailTimeout == "" {
838
+ newServer .FailTimeout = defaultFailTimeout
839
+ }
840
+
841
+ if serverNGX .SlowStart != "" && newServer .SlowStart == "" {
842
+ newServer .SlowStart = defaultSlowStart
843
+ }
844
+
845
+ if serverNGX .Backup != nil && newServer .Backup == nil {
846
+ newServer .Backup = & defaultBackup
847
+ }
848
+
849
+ if serverNGX .Down != nil && newServer .Down == nil {
850
+ newServer .Down = & defaultDown
851
+ }
852
+
853
+ if serverNGX .Weight != nil && newServer .Weight == nil {
854
+ newServer .Weight = & defaultWeight
855
+ }
856
+
857
+ return reflect .DeepEqual (newServer , serverNGX )
858
+ }
859
+
860
+ func determineStreamUpdates (updatedServers []StreamUpstreamServer , nginxServers []StreamUpstreamServer ) (toAdd []StreamUpstreamServer , toRemove []StreamUpstreamServer , toUpdate []StreamUpstreamServer ) {
861
+ for _ , server := range updatedServers {
862
+ updateFound := false
863
+ for _ , serverNGX := range nginxServers {
864
+ if server .Server == serverNGX .Server && ! haveStreamSameParameters (server , serverNGX ) {
865
+ updateFound = true
866
+ break
867
+ }
868
+ }
869
+ if updateFound {
870
+ toUpdate = append (toUpdate , server )
871
+ }
872
+ }
873
+
736
874
for _ , server := range updatedServers {
737
875
found := false
738
876
for _ , serverNGX := range nginxServers {
@@ -1059,7 +1197,7 @@ func (client *NginxClient) modifyKeyValPair(zone string, key string, val string,
1059
1197
1060
1198
path := fmt .Sprintf ("%v/keyvals/%v" , base , zone )
1061
1199
input := KeyValPairs {key : val }
1062
- err := client .patch (path , & input )
1200
+ err := client .patch (path , & input , http . StatusNoContent )
1063
1201
if err != nil {
1064
1202
return fmt .Errorf ("failed to update key value pair for %v/%v zone: %v" , base , zone , err )
1065
1203
}
@@ -1092,7 +1230,7 @@ func (client *NginxClient) deleteKeyValuePair(zone string, key string, stream bo
1092
1230
keyval [key ] = nil
1093
1231
1094
1232
path := fmt .Sprintf ("%v/keyvals/%v" , base , zone )
1095
- err := client .patch (path , & keyval )
1233
+ err := client .patch (path , & keyval , http . StatusNoContent )
1096
1234
if err != nil {
1097
1235
return fmt .Errorf ("failed to remove key values pair for %v/%v zone: %v" , base , zone , err )
1098
1236
}
@@ -1125,3 +1263,43 @@ func (client *NginxClient) deleteKeyValPairs(zone string, stream bool) error {
1125
1263
}
1126
1264
return nil
1127
1265
}
1266
+
1267
+ // UpdateHTTPServer updates the server of the upstream.
1268
+ func (client * NginxClient ) UpdateHTTPServer (upstream string , server UpstreamServer ) error {
1269
+ path := fmt .Sprintf ("http/upstreams/%v/servers/%v" , upstream , server .ID )
1270
+ server .ID = 0
1271
+ err := client .patch (path , & server , http .StatusOK )
1272
+ if err != nil {
1273
+ return fmt .Errorf ("failed to update %v server to %v upstream: %v" , server .Server , upstream , err )
1274
+ }
1275
+
1276
+ return nil
1277
+ }
1278
+
1279
+ // UpdateStreamServer updates the stream server of the upstream.
1280
+ func (client * NginxClient ) UpdateStreamServer (upstream string , server StreamUpstreamServer ) error {
1281
+ path := fmt .Sprintf ("stream/upstreams/%v/servers/%v" , upstream , server .ID )
1282
+ server .ID = 0
1283
+ err := client .patch (path , & server , http .StatusOK )
1284
+ if err != nil {
1285
+ return fmt .Errorf ("failed to update %v stream server to %v upstream: %v" , server .Server , upstream , err )
1286
+ }
1287
+
1288
+ return nil
1289
+ }
1290
+
1291
+ func addPortToServer (server string ) string {
1292
+ if len (strings .Split (server , ":" )) == 2 {
1293
+ return server
1294
+ }
1295
+
1296
+ if len (strings .Split (server , "]:" )) == 2 {
1297
+ return server
1298
+ }
1299
+
1300
+ if strings .HasPrefix (server , "unix:" ) {
1301
+ return server
1302
+ }
1303
+
1304
+ return fmt .Sprintf ("%v:%v" , server , defaultServerPort )
1305
+ }
0 commit comments