From 0a486a7f544a74d95ef05e72f35cd096541752ff Mon Sep 17 00:00:00 2001
From: Maxim Goncharenko <kacejot@fex.net>
Date: Fri, 17 May 2019 14:03:06 +0300
Subject: [PATCH] I have finished implementing validation logic using TDD

---
 pkg/engine/validation.go      |  89 ++++++++++++++++++++++-
 pkg/engine/validation_test.go | 128 +++++++++++++++++++++++++++++++++-
 2 files changed, 213 insertions(+), 4 deletions(-)

diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go
index d5a4c62dac..40850c4e85 100644
--- a/pkg/engine/validation.go
+++ b/pkg/engine/validation.go
@@ -4,6 +4,8 @@ import (
 	"encoding/json"
 	"fmt"
 	"log"
+	"strconv"
+	"strings"
 
 	"github.com/minio/minio/pkg/wildcard"
 
@@ -11,6 +13,19 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
+// TODO: This operators are already implemented in kubernetes
+type Operator string
+
+const (
+	MoreEqual Operator = ">="
+	LessEqual Operator = "<="
+	NotEqual  Operator = "!="
+	More      Operator = ">"
+	Less      Operator = "<"
+)
+
+// TODO: Refactor using State pattern
+
 // Validate handles validating admission request
 // Checks the target resourse for rules defined in the policy
 func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) bool {
@@ -228,8 +243,80 @@ func checkForWildcard(value, pattern string) bool {
 	return wildcard.Match(pattern, value)
 }
 
+func checkSingleOperator(value float64, operator string) bool {
+	if operatorVal, err := strconv.ParseFloat(operator, 64); err == nil {
+		return value == operatorVal
+	}
+
+	if len(operator) < 2 {
+		fmt.Printf("Validating error: operator can't have less than 2 characters: %s\n", operator)
+		return false
+	}
+
+	if operator[:len(MoreEqual)] == string(MoreEqual) {
+		operatorVal, err := strconv.ParseFloat(operator[len(MoreEqual):len(operator)], 64)
+		if err != nil {
+			fmt.Printf("Validating error: failed to parse operator value: %s\n", operator)
+			return false
+		}
+
+		return value >= operatorVal
+	}
+
+	if operator[:len(LessEqual)] == string(LessEqual) {
+		operatorVal, err := strconv.ParseFloat(operator[len(LessEqual):len(operator)], 64)
+		if err != nil {
+			fmt.Printf("Validating error: failed to parse operator value: %s\n", operator)
+			return false
+		}
+
+		return value <= operatorVal
+	}
+
+	if operator[:len(More)] == string(More) {
+		operatorVal, err := strconv.ParseFloat(operator[len(More):len(operator)], 64)
+		if err != nil {
+			fmt.Printf("Validating error: failed to parse operator value: %s\n", operator)
+			return false
+		}
+
+		return value > operatorVal
+	}
+
+	if operator[:len(Less)] == string(Less) {
+		operatorVal, err := strconv.ParseFloat(operator[len(Less):len(operator)], 64)
+		if err != nil {
+			fmt.Printf("Validating error: failed to parse operator value: %s\n", operator)
+			return false
+		}
+
+		return value < operatorVal
+	}
+
+	if operator[:len(NotEqual)] == string(NotEqual) {
+		operatorVal, err := strconv.ParseFloat(operator[len(NotEqual):len(operator)], 64)
+		if err != nil {
+			fmt.Printf("Validating error: failed to parse operator value: %s\n", operator)
+			return false
+		}
+
+		return value != operatorVal
+	}
+
+	return false
+}
+
 func checkForOperator(value float64, pattern string) bool {
-	return true
+	operators := strings.Split(pattern, "|")
+
+	for _, operator := range operators {
+		operator = strings.Replace(operator, " ", "", -1)
+		if checkSingleOperator(value, operator) {
+			return true
+		}
+	}
+
+	return false
 }
 
 func wrappedWithParentheses(str string) bool {
diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go
index 5302fd6988..68d07ca73e 100644
--- a/pkg/engine/validation_test.go
+++ b/pkg/engine/validation_test.go
@@ -102,16 +102,138 @@ func TestCheckSingleValue_CheckFloat(t *testing.T) {
 	assert.Assert(t, !checkSingleValue(value, pattern))
 }
 
-func TestCheckSingleValue_CheckOperatorMore(t *testing.T) {
-	pattern := ">10"
+func TestCheckSingleValue_CheckOperatorMoreEqual(t *testing.T) {
+	pattern := "  >=    89 "
 	value := 89
 	assert.Assert(t, checkSingleValue(value, pattern))
 
-	pattern = ">10"
+	pattern = ">=10.0001"
 	floatValue := 89.901
 	assert.Assert(t, checkSingleValue(floatValue, pattern))
 }
 
+func TestCheckSingleValue_CheckOperatorMoreEqualFail(t *testing.T) {
+	pattern := "  >=    90 "
+	value := 89
+	assert.Assert(t, !checkSingleValue(value, pattern))
+
+	pattern = ">=910.0001"
+	floatValue := 89.901
+	assert.Assert(t, !checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorLessEqual(t *testing.T) {
+	pattern := "   <=  1 "
+	value := 1
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	pattern = "<=10.0001"
+	floatValue := 1.901
+	assert.Assert(t, checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorLessEqualFail(t *testing.T) {
+	pattern := "   <=  0.1558 "
+	value := 1
+	assert.Assert(t, !checkSingleValue(value, pattern))
+
+	pattern = "<=10.0001"
+	floatValue := 12.901
+	assert.Assert(t, !checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorMore(t *testing.T) {
+	pattern := "   >  10 "
+	value := 89
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	pattern = ">10.0001"
+	floatValue := 89.901
+	assert.Assert(t, checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorMoreFail(t *testing.T) {
+	pattern := "   >  89 "
+	value := 89
+	assert.Assert(t, !checkSingleValue(value, pattern))
+
+	pattern = ">910.0001"
+	floatValue := 89.901
+	assert.Assert(t, !checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorLess(t *testing.T) {
+	pattern := "   <  10 "
+	value := 9
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	pattern = "<10.0001"
+	floatValue := 9.901
+	assert.Assert(t, checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorLessFail(t *testing.T) {
+	pattern := "   <  10 "
+	value := 10
+	assert.Assert(t, !checkSingleValue(value, pattern))
+
+	pattern = "<10.0001"
+	floatValue := 19.901
+	assert.Assert(t, !checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorNotEqual(t *testing.T) {
+	pattern := "   !=  10 "
+	value := 9.99999
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	pattern = "!=10.0001"
+	floatValue := 10.0000
+	assert.Assert(t, checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorNotEqualFail(t *testing.T) {
+	pattern := "   !=  9.99999 "
+	value := 9.99999
+	assert.Assert(t, !checkSingleValue(value, pattern))
+
+	pattern = "!=10"
+	floatValue := 10
+	assert.Assert(t, !checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorEqual(t *testing.T) {
+	pattern := "     10.000001 "
+	value := 10.000001
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	pattern = "10.000000"
+	floatValue := 10
+	assert.Assert(t, checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckOperatorEqualFail(t *testing.T) {
+	pattern := "     10.000000 "
+	value := 10.000001
+	assert.Assert(t, !checkSingleValue(value, pattern))
+
+	pattern = "10.000001"
+	floatValue := 10
+	assert.Assert(t, !checkSingleValue(floatValue, pattern))
+}
+
+func TestCheckSingleValue_CheckSeveralOperators(t *testing.T) {
+	pattern := " <-1  |  10.000001 "
+	value := 10.000001
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	value = -30
+	assert.Assert(t, checkSingleValue(value, pattern))
+
+	value = 5
+	assert.Assert(t, !checkSingleValue(value, pattern))
+}
+
 func TestCheckSingleValue_CheckWildcard(t *testing.T) {
 	pattern := "nirmata_*"
 	value := "nirmata_awesome"