Skip to content
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
175 changes: 59 additions & 116 deletions util/ValidatorHelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ type resourceParser struct {
var memoryParser *resourceParser
var cpuParser *resourceParser

func getResourcesLimitsKeys(envoyProxy bool) []string {
if envoyProxy {
return []string{"envoyproxy", "resources", "limits"}
} else {
return []string{"resources", "limits"}
}
}
func getResourcesRequestsKeys(envoyProxy bool) []string {
if envoyProxy {
return []string{"envoyproxy", "resources", "requests"}
} else {
return []string{"resources", "requests"}
}
}

func validateAndBuildResourcesAssignment(dat map[string]interface{}, validationKeys []string) (validatedMap map[string]interface{}) {
var test map[string]interface{}
test = dat
for _, validationKey := range validationKeys {
if test[validationKey] != nil {
test = test[validationKey].(map[string]interface{})
} else {
return map[string]interface{}{"cpu": "0", "memory": "0"}
}
}
return test
}

func MemoryToNumber(memory string) (float64, error) {
if memoryParser == nil {
pattern := "(\\d*e?\\d*)(Ei?|Pi?|Ti?|Gi?|Mi?|Ki?|$)"
Expand Down Expand Up @@ -119,14 +147,11 @@ func ParseFloat(str string) (float64, error) {
}

func CompareLimitsRequests(dat map[string]interface{}) (bool, error) {
limit, ok := dat["resources"].(map[string]interface{})["limits"].(map[string]interface{})
if !ok {
return false, errors.New("resources.limits is required")
}
envoproxyLimit, ok := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{})
if !ok {
return false, errors.New("envoproxy.resources.limits is required")
if dat == nil {
return true, nil
}
limit := validateAndBuildResourcesAssignment(dat, getResourcesLimitsKeys(false))
envoproxyLimit := validateAndBuildResourcesAssignment(dat, getResourcesLimitsKeys(true))
checkCPUlimit, ok := limit["cpu"]
if !ok {
return false, errors.New("resources.limits.cpu is required")
Expand All @@ -143,14 +168,8 @@ func CompareLimitsRequests(dat map[string]interface{}) (bool, error) {
if !ok {
return false, errors.New("envoyproxy.resources.limits.memory is required")
}
request, ok := dat["resources"].(map[string]interface{})["requests"].(map[string]interface{})
if !ok {
return true, nil
}
envoproxyRequest, ok := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})
if !ok {
return true, nil
}
request := validateAndBuildResourcesAssignment(dat, getResourcesRequestsKeys(false))
envoproxyRequest := validateAndBuildResourcesAssignment(dat, getResourcesRequestsKeys(true))
checkCPURequests, ok := request["cpu"]
if !ok {
return true, nil
Expand Down Expand Up @@ -201,125 +220,43 @@ func CompareLimitsRequests(dat map[string]interface{}) (bool, error) {
if err != nil {
return false, err
}

if (envoproxyCPULimit < envoproxyCPURequest){
if envoproxyCPULimit < envoproxyCPURequest && envoproxyCPULimit != 0 {
return false, errors.New("envoyproxy.resources.limits.cpu must be greater than or equal to envoyproxy.resources.requests.cpu")
}else if (envoproxyMemoryLimit < envoproxyMemoryRequest){
} else if envoproxyMemoryLimit < envoproxyMemoryRequest && envoproxyMemoryLimit != 0 {
return false, errors.New("envoyproxy.resources.limits.memory must be greater than or equal to envoyproxy.resources.requests.memory")
}else if (cpuLimit < cpuRequest) {
} else if cpuLimit < cpuRequest && cpuLimit != 0 {
return false, errors.New("resources.limits.cpu must be greater than or equal to resources.requests.cpu")
}else if (memoryLimit < memoryRequest) {
} else if memoryLimit < memoryRequest && memoryLimit != 0 {
return false, errors.New("resources.limits.memory must be greater than or equal to resources.requests.memory")
}
return true, nil

}

func AutoScale(dat map[string]interface{}) (bool, error) {
autoscaleEnabled, ok := dat["autoscaling"].(map[string]interface{})["enabled"]
if !ok {
if dat == nil {
return true, nil
}
if autoscaleEnabled.(bool) {
limit, ok := dat["resources"].(map[string]interface{})["limits"].(map[string]interface{})
if !ok {
return false, errors.New("resources.limits is required")
}
envoproxyLimit, ok := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{})
if !ok {
return false, errors.New("envoproxy.resources.limits is required")
}
checkCPUlimit, ok := limit["cpu"]
if !ok {
return false, errors.New("resources.limits.cpu is required")
}
checkMemorylimit, ok := limit["memory"]
if !ok {
return false, errors.New("resources.limits.memory is required")
}
checkEnvoproxyCPUlimit, ok := envoproxyLimit["cpu"]
if !ok {
return false, errors.New("envoyproxy.resources.limits.cpu is required")
}
checkEnvoproxyMemorylimit, ok := envoproxyLimit["memory"]
if dat["autoscaling"]!=nil {
autoScaleEnabled, ok := dat["autoscaling"].(map[string]interface{})["enabled"]
if !ok {
return false, errors.New("envoyproxy.resources.limits.memory is required")
}
request, ok := dat["resources"].(map[string]interface{})["requests"].(map[string]interface{})
if !ok {
return false, errors.New("resources.requests is required")
}
envoproxyRequest, ok := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})
if !ok {
return false, errors.New("envoyproxy.resources.requests is required")
}
checkCPURequests, ok := request["cpu"]
if !ok {
return false, errors.New("resources.requests.cpu is required")
}
checkMemoryRequests, ok := request["memory"]
if !ok {
return false, errors.New("resources.requests.memory is required")
}
checkEnvoproxyCPURequests, ok := envoproxyRequest["cpu"]
if !ok {
return false, errors.New("envoyproxy.resources.requests.cpu is required")
}
checkEnvoproxyMemoryRequests, ok := envoproxyRequest["memory"]
if !ok {
return false, errors.New("envoyproxy.resources.requests.memory is required")
}

cpuLimit, err := CpuToNumber(checkCPUlimit.(string))
if err != nil {
return false, err
}
memoryLimit, err := MemoryToNumber(checkMemorylimit.(string))
if err != nil {
return false, err
}
cpuRequest, err := CpuToNumber(checkCPURequests.(string))
if err != nil {
return false, err
}
memoryRequest, err := MemoryToNumber(checkMemoryRequests.(string))
if err != nil {
return false, err
}

envoproxyCPULimit, err := CpuToNumber(checkEnvoproxyCPUlimit.(string))
if err != nil {
return false, err
}
envoproxyMemoryLimit, err := MemoryToNumber(checkEnvoproxyMemorylimit.(string))
if err != nil {
return false, err
}
envoproxyCPURequest, err := CpuToNumber(checkEnvoproxyCPURequests.(string))
if err != nil {
return false, err
}
envoproxyMemoryRequest, err := MemoryToNumber(checkEnvoproxyMemoryRequests.(string))
if err != nil {
return false, err
}

if (envoproxyCPULimit < envoproxyCPURequest){
return false, errors.New("envoyproxy.resources.limits.cpu must be greater than or equal to envoyproxy.resources.requests.cpu")
}else if (envoproxyMemoryLimit < envoproxyMemoryRequest){
return false, errors.New("envoyproxy.resources.limits.memory must be greater than or equal to envoyproxy.resources.requests.memory")
}else if (cpuLimit < cpuRequest) {
return false, errors.New("resources.limits.cpu must be greater than or equal to resources.requests.cpu")
}else if (memoryLimit < memoryRequest) {
return false, errors.New("resources.limits.memory must be greater than or equal to resources.requests.memory")
}else {
return true, nil
}
} else {
return true, nil
if autoScaleEnabled.(bool) {
minReplicas, okMin := dat["autoscaling"].(map[string]interface{})["MinReplicas"]
maxReplicas, okMax := dat["autoscaling"].(map[string]interface{})["MaxReplicas"]
if !okMin || !okMax{
return false, errors.New("autoscaling.MinReplicas and autoscaling.MaxReplicas are mandatory fields")
}
if minReplicas.(int) > maxReplicas.(int){
return false, errors.New("autoscaling.MinReplicas can not be greater than autoscaling.MaxReplicas")
}
}
}
return true,nil
}


var (
CpuUnitChecker, _ = regexp.Compile("^([0-9.]+)m$")
NoCpuUnitChecker, _ = regexp.Compile("^([0-9.]+)$")
Expand All @@ -331,6 +268,9 @@ var (
)

func (f CpuChecker) IsFormat(input interface{}) bool {
if input == nil {
return false
}
asString, ok := input.(string)
if !ok {
return false
Expand All @@ -346,6 +286,9 @@ func (f CpuChecker) IsFormat(input interface{}) bool {
}

func (f MemoryChecker) IsFormat(input interface{}) bool {
if input == nil {
return false
}
asString, ok := input.(string)
if !ok {
return false
Expand Down
146 changes: 146 additions & 0 deletions util/ValidatorHelper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package util

import "testing"

func TestCompareLimitsRequests(t *testing.T) {
requests := "requests"
limits := "limits"
resources := "resources"
cpu := "cpu"
memory := "memory"
type args struct {
dat map[string]interface{}
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "empty base object",
args: args{dat: nil},
want: true,
wantErr: false,
},
{
name: "empty resources object",
args: args{dat: map[string]interface{}{resources: nil}},
want: true,
wantErr: false,
},
{
name: "empty resources requests limits object",
args: args{dat: map[string]interface{}{resources: map[string]interface{}{limits: nil, requests: nil}}},
want: true,
wantErr: false,
},
{
name: "non-empty resources limits object",
args: args{dat: map[string]interface{}{resources: map[string]interface{}{limits: map[string]interface{}{cpu: "10Gi", memory: ".1" }, requests: nil}}},
want: true,
wantErr: false,
},
{
name: "non-empty resources requests object",
args: args{dat: map[string]interface{}{resources: map[string]interface{}{limits: nil, requests: map[string]interface{}{cpu: "10Gi", memory: ".1" }}}},
want: true,
wantErr: false,
},
{
name: "non-empty and equal resources limits and requests object",
args: args{dat: map[string]interface{}{resources: map[string]interface{}{limits: map[string]interface{}{cpu: "10Gi", memory: ".1" }, requests: map[string]interface{}{cpu: "10Gi", memory: ".1" }}}},
want: true,
wantErr: false,
},
{
name: "negative: non-empty and not equal resources limits and requests object",
args: args{dat: map[string]interface{}{resources: map[string]interface{}{limits: map[string]interface{}{cpu: "10Gi", memory: ".1" }, requests: map[string]interface{}{cpu: "11Gi", memory: ".15" }}}},
want: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CompareLimitsRequests(tt.args.dat)
if (err != nil) != tt.wantErr {
t.Errorf("CompareLimitsRequests() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("CompareLimitsRequests() got = %v, want %v", got, tt.want)
}
})
}
}

func TestAutoScale(t *testing.T) {
autoScaling := "autoscaling"
minReplicas := "MinReplicas"
maxReplicas := "MaxReplicas"
enabled := "enabled"
type args struct {
dat map[string]interface{}
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "empty base object",
args: args{dat: nil},
want: true,
wantErr: false,
},
{
name: "empty autoscaling object",
args: args{dat: map[string]interface{}{autoScaling: nil}},
want: true,
wantErr: false,
},
{
name: "negative : non-empty autoscaling empty enabled minReplicas maxReplicas object",
args: args{dat: map[string]interface{}{autoScaling: map[string]interface{}{}}},
want: true,
wantErr: false,
},
{
name: "non-empty autoscaling enabled minReplicas maxReplicas object",
args: args{dat: map[string]interface{}{autoScaling: map[string]interface{}{enabled: false,minReplicas: 10, maxReplicas:11}}},
want: true,
wantErr: false,
},
{
name: "non-empty autoscaling enabled minReplicas empty maxReplicas object",
args: args{dat: map[string]interface{}{autoScaling: map[string]interface{}{enabled: false,minReplicas: 11}}},
want: true,
wantErr: false,
},
{
name: "non-empty autoscaling minReplicas maxReplicas object empty enabled",
args: args{dat: map[string]interface{}{autoScaling: map[string]interface{}{minReplicas: 10, maxReplicas:11}}},
want: true,
wantErr: false,
},
{
name: "negative: non-empty and greater minReplicas than maxReplicas object",
args: args{dat: map[string]interface{}{autoScaling: map[string]interface{}{enabled: true, minReplicas: 10, maxReplicas:9}}},
want: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := AutoScale(tt.args.dat)
if (err != nil) != tt.wantErr {
t.Errorf("AutoScale() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("AutoScale() got = %v, want %v", got, tt.want)
}
})
}
}
Loading