Skip to content

Allow upstream parameters update #33

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
Nov 15, 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
37 changes: 19 additions & 18 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

[[constraint]]
name = "github.com/nginxinc/nginx-plus-go-client"
version = "0.4.0"
version = "0.6.0"

# https://github.com/Azure/go-autorest/issues/439#issuecomment-521732075
[[override]]
Expand Down
24 changes: 22 additions & 2 deletions cmd/sync/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ func (client *AWSClient) GetUpstreams() []Upstream {
Port: awsU.Port,
Kind: awsU.Kind,
ScalingGroup: awsU.AutoscalingGroup,
MaxConns: &awsU.MaxConns,
MaxFails: &awsU.MaxFails,
FailTimeout: awsU.FailTimeout,
SlowStart: awsU.SlowStart,
}
upstreams = append(upstreams, u)
}
Expand Down Expand Up @@ -108,7 +112,7 @@ func parseAWSConfig(data []byte) (*awsConfig, error) {
func (client *AWSClient) CheckIfScalingGroupExists(name string) (bool, error) {
params := &ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
&ec2.Filter{
{
Name: aws.String("tag:aws:autoscaling:groupName"),
Values: []*string{
aws.String(name),
Expand All @@ -129,7 +133,7 @@ func (client *AWSClient) CheckIfScalingGroupExists(name string) (bool, error) {
func (client *AWSClient) GetPrivateIPsForScalingGroup(name string) ([]string, error) {
params := &ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
&ec2.Filter{
{
Name: aws.String("tag:aws:autoscaling:groupName"),
Values: []*string{
aws.String(name),
Expand Down Expand Up @@ -171,6 +175,10 @@ type awsUpstream struct {
AutoscalingGroup string `yaml:"autoscaling_group"`
Port int
Kind string
MaxConns int `yaml:"max_conns"`
MaxFails int `yaml:"max_fails"`
FailTimeout string `yaml:"fail_timeout"`
SlowStart string `yaml:"slow_start"`
}

func validateAWSConfig(cfg *awsConfig) error {
Expand All @@ -195,6 +203,18 @@ func validateAWSConfig(cfg *awsConfig) error {
if ups.Kind == "" || !(ups.Kind == "http" || ups.Kind == "stream") {
return fmt.Errorf(upstreamKindErrorMsgFormat, ups.Name)
}
if ups.MaxConns < 0 {
return fmt.Errorf(upstreamMaxConnsErrorMsgFmt, ups.MaxConns)
}
if ups.MaxFails < 0 {
return fmt.Errorf(upstreamMaxFailsErrorMsgFmt, ups.MaxFails)
}
if !isValidTime(ups.FailTimeout) {
return fmt.Errorf(upstreamFailTimeoutErrorMsgFmt, ups.FailTimeout)
}
if !isValidTime(ups.SlowStart) {
return fmt.Errorf(upstreamSlowStartErrorMsgFmt, ups.SlowStart)
}
}

return nil
Expand Down
16 changes: 16 additions & 0 deletions cmd/sync/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ func getInvalidAWSConfigInput() []*testInputAWS {
invalidUpstreamKindCfg.Upstreams[0].Kind = ""
input = append(input, &testInputAWS{invalidUpstreamKindCfg, "invalid kind of the upstream"})

invalidUpstreamMaxConnsCfg := getValidAWSConfig()
invalidUpstreamMaxConnsCfg.Upstreams[0].MaxConns = -10
input = append(input, &testInputAWS{invalidUpstreamMaxConnsCfg, "invalid max_conns of the upstream"})

invalidUpstreamMaxFailsCfg := getValidAWSConfig()
invalidUpstreamMaxFailsCfg.Upstreams[0].MaxFails = -10
input = append(input, &testInputAWS{invalidUpstreamMaxFailsCfg, "invalid max_fails of the upstream"})

invalidUpstreamFailTimeoutCfg := getValidAWSConfig()
invalidUpstreamFailTimeoutCfg.Upstreams[0].FailTimeout = "-10s"
input = append(input, &testInputAWS{invalidUpstreamFailTimeoutCfg, "invalid fail_timeout of the upstream"})

invalidUpstreamSlowStartCfg := getValidAWSConfig()
invalidUpstreamSlowStartCfg.Upstreams[0].SlowStart = "-10s"
input = append(input, &testInputAWS{invalidUpstreamSlowStartCfg, "invalid slow_start of the upstream"})

return input
}

Expand Down
30 changes: 25 additions & 5 deletions cmd/sync/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ func (client *AzureClient) GetUpstreams() []Upstream {
Port: azureU.Port,
Kind: azureU.Kind,
ScalingGroup: azureU.VMScaleSet,
MaxConns: &azureU.MaxConns,
MaxFails: &azureU.MaxFails,
FailTimeout: azureU.FailTimeout,
SlowStart: azureU.SlowStart,
}
upstreams = append(upstreams, u)
}
Expand All @@ -149,10 +153,14 @@ type azureConfig struct {
}

type azureUpstream struct {
Name string
VMScaleSet string `yaml:"virtual_machine_scale_set"`
Port int
Kind string
Name string
VMScaleSet string `yaml:"virtual_machine_scale_set"`
Port int
Kind string
MaxConns int `yaml:"max_conns"`
MaxFails int `yaml:"max_fails"`
FailTimeout string `yaml:"fail_timeout"`
SlowStart string `yaml:"slow_start"`
}

func validateAzureConfig(cfg *azureConfig) error {
Expand All @@ -165,7 +173,7 @@ func validateAzureConfig(cfg *azureConfig) error {
}

if len(cfg.Upstreams) == 0 {
return fmt.Errorf("There is no upstreams found in the config file")
return fmt.Errorf("There are no upstreams found in the config file")
}

for _, ups := range cfg.Upstreams {
Expand All @@ -181,6 +189,18 @@ func validateAzureConfig(cfg *azureConfig) error {
if ups.Kind == "" || !(ups.Kind == "http" || ups.Kind == "stream") {
return fmt.Errorf(upstreamKindErrorMsgFormat, ups.Name)
}
if ups.MaxConns < 0 {
return fmt.Errorf(upstreamMaxConnsErrorMsgFmt, ups.MaxConns)
}
if ups.MaxFails < 0 {
return fmt.Errorf(upstreamMaxFailsErrorMsgFmt, ups.MaxFails)
}
if !isValidTime(ups.FailTimeout) {
return fmt.Errorf(upstreamFailTimeoutErrorMsgFmt, ups.FailTimeout)
}
if !isValidTime(ups.SlowStart) {
return fmt.Errorf(upstreamSlowStartErrorMsgFmt, ups.SlowStart)
}
}
return nil
}
16 changes: 16 additions & 0 deletions cmd/sync/azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ func getInvalidAzureConfigInput() []*testInputAzure {
invalidUpstreamKindCfg.Upstreams[0].Kind = ""
input = append(input, &testInputAzure{invalidUpstreamKindCfg, "invalid kind of the upstream"})

invalidUpstreamMaxConnsCfg := getValidAzureConfig()
invalidUpstreamMaxConnsCfg.Upstreams[0].MaxConns = -10
input = append(input, &testInputAzure{invalidUpstreamMaxConnsCfg, "invalid max_conns of the upstream"})

invalidUpstreamMaxFailsCfg := getValidAzureConfig()
invalidUpstreamMaxFailsCfg.Upstreams[0].MaxFails = -10
input = append(input, &testInputAzure{invalidUpstreamMaxFailsCfg, "invalid max_fails of the upstream"})

invalidUpstreamFailTimeoutCfg := getValidAzureConfig()
invalidUpstreamFailTimeoutCfg.Upstreams[0].FailTimeout = "-10s"
input = append(input, &testInputAzure{invalidUpstreamFailTimeoutCfg, "invalid fail_timeout of the upstream"})

invalidUpstreamSlowStartCfg := getValidAzureConfig()
invalidUpstreamSlowStartCfg.Upstreams[0].SlowStart = "-10s"
input = append(input, &testInputAzure{invalidUpstreamSlowStartCfg, "invalid slow_start of the upstream"})

return input
}

Expand Down
4 changes: 4 additions & 0 deletions cmd/sync/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ type Upstream struct {
Port int
ScalingGroup string
Kind string
MaxConns *int
MaxFails *int
FailTimeout string
SlowStart string
}
4 changes: 4 additions & 0 deletions cmd/sync/errormessages.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ const upstreamNameErrorMsg = "The mandatory field name is either empty or missin
const upstreamErrorMsgFormat = "The mandatory field %v is either empty or missing for the upstream %v in the config file"
const upstreamPortErrorMsgFormat = "The mandatory field port is either zero or missing for the upstream %v in the config file"
const upstreamKindErrorMsgFormat = "The mandatory field kind is either not equal to http or tcp or missing for the upstream %v in the config file"
const upstreamMaxConnsErrorMsgFmt = "The field max_conns has invalid value %v in the config file"
const upstreamMaxFailsErrorMsgFmt = "The field max_fails has invalid value %v in the config file"
const upstreamFailTimeoutErrorMsgFmt = "The field fail_timeout has invalid value %v in the config file"
const upstreamSlowStartErrorMsgFmt = "The field slow_start has invalid value %v in the config file"
26 changes: 16 additions & 10 deletions cmd/sync/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,38 +113,44 @@ func main() {
for _, ip := range ips {
backend := fmt.Sprintf("%v:%v", ip, upstream.Port)
upsServers = append(upsServers, nginx.UpstreamServer{
Server: backend,
MaxFails: 1,
Server: backend,
MaxConns: upstream.MaxConns,
MaxFails: upstream.MaxFails,
FailTimeout: upstream.FailTimeout,
SlowStart: upstream.SlowStart,
})
}

added, removed, err := nginxClient.UpdateHTTPServers(upstream.Name, upsServers)
added, removed, updated, err := nginxClient.UpdateHTTPServers(upstream.Name, upsServers)
if err != nil {
log.Printf("Couldn't update HTTP servers in NGINX: %v", err)
continue
}

if len(added) > 0 || len(removed) > 0 {
log.Printf("Updated HTTP servers of %v; Added: %v, Removed: %v", upstream, added, removed)
if len(added) > 0 || len(removed) > 0 || len(updated) > 0 {
log.Printf("Updated HTTP servers of %v; Added: %+v, Removed: %+v, Updated: %+v", upstream, added, removed, updated)
}
} else {
var upsServers []nginx.StreamUpstreamServer
for _, ip := range ips {
backend := fmt.Sprintf("%v:%v", ip, upstream.Port)
upsServers = append(upsServers, nginx.StreamUpstreamServer{
Server: backend,
MaxFails: 1,
Server: backend,
MaxConns: upstream.MaxConns,
MaxFails: upstream.MaxFails,
FailTimeout: upstream.FailTimeout,
SlowStart: upstream.SlowStart,
})
}

added, removed, err := nginxClient.UpdateStreamServers(upstream.Name, upsServers)
added, removed, updated, err := nginxClient.UpdateStreamServers(upstream.Name, upsServers)
if err != nil {
log.Printf("Couldn't update Steam servers in NGINX: %v", err)
continue
}

if len(added) > 0 || len(removed) > 0 {
log.Printf("Updated Stream servers of %v; Added: %v, Removed: %v", upstream, added, removed)
if len(added) > 0 || len(removed) > 0 || len(updated) > 0 {
log.Printf("Updated Stream servers of %v; Added: %+v, Removed: %+v, Updated: %+v", upstream, added, removed, updated)
}
}

Expand Down
31 changes: 31 additions & 0 deletions cmd/sync/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"regexp"
"strings"
)

// http://nginx.org/en/docs/syntax.html
var validTimeSuffixes = []string{
"ms",
"s",
"m",
"h",
"d",
"w",
"M",
"y",
}

var durationEscaped = strings.Join(validTimeSuffixes, "|")
var validNginxTime = regexp.MustCompile(`^([0-9]+([` + durationEscaped + `]?){0,1} *)+$`)

func isValidTime(time string) bool {
if time == "" {
return true
}

time = strings.TrimSpace(time)

return validNginxTime.MatchString(time)
}
21 changes: 21 additions & 0 deletions cmd/sync/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import "testing"

func TestIsValidTime(t *testing.T) {
var testsWithValidInput = []string{"1", "1m10s", "11 11", "5m 30s", "1s", "100m", "5w", "15m", "11M", "3h", "100y", "600"}
var invalidInput = []string{"ss", "rM", "m0m", "s1s", "-5s", "1L"}

for _, test := range testsWithValidInput {
valid := isValidTime(test)
if !valid {
t.Errorf("isValidTime(%q) returned false for valid input.", test)
}
}
for _, test := range invalidInput {
valid := isValidTime(test)
if valid {
t.Errorf("isValidTime(%q) returned true for invalid input.", test)
}
}
}
Loading