Skip to content
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

Add upstream parameters in nginx-asg-sync #30

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 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
25 changes: 23 additions & 2 deletions cmd/sync/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
yaml "gopkg.in/yaml.v2"
"gopkg.in/yaml.v2"
)

// AWSClient allows you to get the list of IP addresses of instanes of an Auto Scaling group. It implements the CloudProvider interface
Expand Down Expand Up @@ -65,8 +65,12 @@ func (client *AWSClient) GetUpstreams() []Upstream {
u := Upstream{
Name: awsU.Name,
Port: awsU.Port,
Kind: awsU.Kind,
ScalingGroup: awsU.AutoscalingGroup,
Kind: awsU.Kind,
MaxConns: awsU.MaxConns,
MaxFails: &awsU.MaxFails,
FailTimeout: awsU.FailTimeout,
SlowStart: awsU.SlowStart,
}
upstreams = append(upstreams, u)
}
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,19 @@ 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(upstreamMaxConnsErrorMsg, ups.MaxConns)
}
if ups.MaxFails < 0 {
return fmt.Errorf(upstreamMaxFailsErrorMsg, ups.MaxFails)
}
if err := validateTime(ups.FailTimeout); err != nil {
return fmt.Errorf(upstreamFailTimeoutErrorMsg, ups.FailTimeout, err)
}
if err := validateTime(ups.SlowStart); err != nil {
return fmt.Errorf(upstreamSlowStartErrorMsg, ups.SlowStart, err)
}

}

return nil
Expand Down
20 changes: 20 additions & 0 deletions cmd/sync/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ func getValidAWSConfig() *awsConfig {
AutoscalingGroup: "backend-group",
Port: 80,
Kind: "http",
MaxConns: 1000,
MaxFails: 10,
FailTimeout: "5s",
SlowStart: "30s",
},
}
cfg := awsConfig{
Expand Down Expand Up @@ -51,6 +55,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
32 changes: 26 additions & 6 deletions cmd/sync/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
"github.com/Azure/azure-sdk-for-go/profiles/latest/network/mgmt/network"
"github.com/Azure/go-autorest/autorest/azure/auth"
yaml "gopkg.in/yaml.v2"
"gopkg.in/yaml.v2"
)

// AzureClient allows you to get the list of IP addresses of VirtualMachines of a VirtualMachine Scale Set. It implements the CloudProvider interface
Expand Down Expand Up @@ -134,8 +134,12 @@ func (client *AzureClient) GetUpstreams() []Upstream {
u := Upstream{
Name: azureU.Name,
Port: azureU.Port,
Kind: azureU.Kind,
ScalingGroup: azureU.VMScaleSet,
Kind: azureU.Kind,
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 Down Expand Up @@ -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(upstreamMaxConnsErrorMsg, ups.MaxConns)
}
if ups.MaxFails < 0 {
return fmt.Errorf(upstreamMaxFailsErrorMsg, ups.MaxFails)
}
if err := validateTime(ups.FailTimeout); err != nil {
return fmt.Errorf(upstreamFailTimeoutErrorMsg, ups.FailTimeout, err)
}
if err := validateTime(ups.SlowStart); err != nil {
return fmt.Errorf(upstreamSlowStartErrorMsg, ups.SlowStart, err)
}
}
return nil
}
28 changes: 24 additions & 4 deletions cmd/sync/azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ type testInputAzure struct {
func getValidAzureConfig() *azureConfig {
upstreams := []azureUpstream{
{
Name: "backend1",
VMScaleSet: "backend-group",
Port: 80,
Kind: "http",
Name: "backend1",
VMScaleSet: "backend-group",
Port: 80,
Kind: "http",
MaxConns: 1000,
MaxFails: 10,
FailTimeout: "5s",
SlowStart: "30s",
},
}
cfg := azureConfig{
Expand Down Expand Up @@ -60,6 +64,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
6 changes: 5 additions & 1 deletion cmd/sync/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"time"

yaml "gopkg.in/yaml.v2"
"gopkg.in/yaml.v2"
)

// commonConfig stores the configuration parameters common to all providers
Expand Down 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 upstreamMaxConnsErrorMsg = "The field max_conns has invalid value %v, must be positive or zero in the config file"
const upstreamMaxFailsErrorMsg = "The field max_fails has invalid value %v, must be positive or zero in the config file"
const upstreamFailTimeoutErrorMsg = "The field fail_timeout has invalid value %v and returned errors %v"
const upstreamSlowStartErrorMsg = "The field slow_start has invalid value %v and returned errors %v"
38 changes: 34 additions & 4 deletions cmd/sync/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ 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: setPositiveInt(upstream.MaxConns, 0),
MaxFails: setPositiveIntOrZeroFromPointer(upstream.MaxFails, 1),
FailTimeout: setTime(upstream.FailTimeout, "10s"),
SlowStart: setTime(upstream.SlowStart, "0s"),
})
}

Expand All @@ -132,8 +135,11 @@ func main() {
for _, ip := range ips {
backend := fmt.Sprintf("%v:%v", ip, upstream.Port)
upsServers = append(upsServers, nginx.StreamUpstreamServer{
Server: backend,
MaxFails: 1,
Server: backend,
MaxConns: setPositiveInt(upstream.MaxConns, 0),
MaxFails: setPositiveIntOrZeroFromPointer(upstream.MaxFails, 1),
FailTimeout: setTime(upstream.FailTimeout, "10s"),
SlowStart: setTime(upstream.SlowStart, "0s"),
})
}

Expand All @@ -158,3 +164,27 @@ func main() {
}
}
}

func setPositiveInt(value int, defaultValue int) int {
if value == 0 {
return defaultValue
}

return value
}

func setPositiveIntOrZeroFromPointer(value *int, defaultValue int) int {
if value == nil {
return defaultValue
}

return *value
}

func setTime(time string, defaultTime string) string {
if time == "" {
return defaultTime
}

return time
}
48 changes: 48 additions & 0 deletions cmd/sync/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import "testing"

func TestSetPositiveInt(t *testing.T) {
defaultValue := 0
value := setPositiveInt(10, defaultValue)
if value == 0 {
t.Errorf(" setPositiveInt() should return value %v but returned invalid value %v", value, defaultValue)
}

defaultValue = 1
value = setPositiveInt(0, defaultValue)
if value != 1 {
t.Errorf(" setPositiveInt() should return default value %v but returned invalid value %v", defaultValue, value)
}
}

func TestSetTime(t *testing.T) {
defaultTime := "10s"
time := setTime("20s", defaultTime)

if time != "20s" {
t.Errorf(" setTime() should return time %v but returned invalid time %v", time, defaultTime)
}

defaultTime = "10s"
time = setTime("", defaultTime)

if time != "10s" {
t.Errorf(" setTime() should return default time %v but returned invalid time %v", defaultTime, time)
}
}

func TestSetPositiveIntOrZeroFromPointer(t *testing.T) {
defaultValue := 1
v := 10
value := setPositiveIntOrZeroFromPointer(&v, defaultValue)
if value == 1 {
t.Errorf(" setPositiveInt() should return value %v but returned invalid value %v", value, defaultValue)
bvighnesha marked this conversation as resolved.
Show resolved Hide resolved
}

defaultValue = 1
value = setPositiveIntOrZeroFromPointer(nil, defaultValue)
if value != 1 {
t.Errorf(" setPositiveInt() should return default value %v but returned invalid value %v", defaultValue, value)
bvighnesha marked this conversation as resolved.
Show resolved Hide resolved
}
}
44 changes: 44 additions & 0 deletions cmd/sync/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"errors"
"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 validateTime(time string) error {
if time == "" {
return nil
}

if _, err := parseTime(time); err != nil {
return err
}

return nil
}

// ParseTime ensures that the string value in the annotation is a valid time.
func parseTime(s string) (string, error) {
s = strings.TrimSpace(s)

if validNginxTime.MatchString(s) {
return s, nil
}
return "", errors.New("Invalid time string")
}
Loading