-
Notifications
You must be signed in to change notification settings - Fork 876
/
equal.go
163 lines (149 loc) · 4.93 KB
/
equal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package operator
import (
"fmt"
"math"
"strconv"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/context"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
wildcard "github.com/kyverno/kyverno/pkg/utils/wildcard"
"k8s.io/apimachinery/pkg/api/resource"
)
// NewEqualHandler returns handler to manage Equal operations
func NewEqualHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler {
return EqualHandler{
ctx: ctx,
log: log,
}
}
// EqualHandler provides implementation to handle NotEqual Operator
type EqualHandler struct {
ctx context.EvalInterface
log logr.Logger
}
// Evaluate evaluates expression with Equal Operator
func (eh EqualHandler) Evaluate(key, value interface{}) bool {
// key and value need to be of same type
switch typedKey := key.(type) {
case bool:
return eh.validateValueWithBoolPattern(typedKey, value)
case int:
return eh.validateValueWithIntPattern(int64(typedKey), value)
case int64:
return eh.validateValueWithIntPattern(typedKey, value)
case float64:
return eh.validateValueWithFloatPattern(typedKey, value)
case string:
return eh.validateValueWithStringPattern(typedKey, value)
case map[string]interface{}:
return eh.validateValueWithMapPattern(typedKey, value)
case []interface{}:
return eh.validateValueWithSlicePattern(typedKey, value)
default:
eh.log.V(2).Info("Unsupported type", "value", typedKey, "type", fmt.Sprintf("%T", typedKey))
return false
}
}
func (eh EqualHandler) validateValueWithSlicePattern(key []interface{}, value interface{}) bool {
if val, ok := value.([]interface{}); ok {
return datautils.DeepEqual(key, val)
}
eh.log.V(2).Info("Expected type []interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
func (eh EqualHandler) validateValueWithMapPattern(key map[string]interface{}, value interface{}) bool {
if val, ok := value.(map[string]interface{}); ok {
return datautils.DeepEqual(key, val)
}
eh.log.V(2).Info("Expected type map[string]interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
func (eh EqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type.
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return durationKey.Seconds() == durationValue.Seconds()
}
// Attempt to extract resource quantity from string.
resourceKey, err := resource.ParseQuantity(key)
if err == nil {
switch typedValue := value.(type) {
case string:
resourceValue, err := resource.ParseQuantity(typedValue)
if err != nil {
eh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
return false
}
return resourceKey.Equal(resourceValue)
}
}
if val, ok := value.(string); ok {
return wildcard.Match(val, key)
}
eh.log.V(2).Info("Expected type string", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
func (eh EqualHandler) validateValueWithFloatPattern(key float64, value interface{}) bool {
switch typedValue := value.(type) {
case int:
// check that float has not fraction
if key == math.Trunc(key) {
return int(key) == typedValue
}
eh.log.V(2).Info("Expected type float, found int", "typedValue", typedValue)
case int64:
// check that float has not fraction
if key == math.Trunc(key) {
return int64(key) == typedValue
}
eh.log.V(2).Info("Expected type float, found int", "typedValue", typedValue)
case float64:
return typedValue == key
case string:
// extract float from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
eh.log.Error(err, "Failed to parse float64 from string")
return false
}
return float64Num == key
default:
eh.log.V(2).Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
return false
}
func (eh EqualHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
typedValue, ok := value.(bool)
if !ok {
eh.log.V(2).Info("Expected type bool", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
return key == typedValue
}
func (eh EqualHandler) validateValueWithIntPattern(key int64, value interface{}) bool {
switch typedValue := value.(type) {
case int:
return int64(typedValue) == key
case int64:
return typedValue == key
case float64:
// check that float has no fraction
if typedValue == math.Trunc(typedValue) {
return int64(typedValue) == key
}
eh.log.V(2).Info("Expected type int, found float", "value", typedValue, "type", fmt.Sprintf("%T", typedValue))
return false
case string:
// extract in64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
eh.log.Error(err, "Failed to parse int64 from string")
return false
}
return int64Num == key
default:
eh.log.V(2).Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
}