Skip to content

Commit 040d5ed

Browse files
authored
KEP-35 Impl Part1: New parameter types and attributes (#1705)
* Added additional types (integer, number, boolean) * Added enum type * Added verification of parameter types in admission webhook * Added descriptive parameters on package level Signed-off-by: Andreas Neumann <[email protected]>
1 parent 5e7546b commit 040d5ed

File tree

19 files changed

+551
-7
lines changed

19 files changed

+551
-7
lines changed

config/crds/kudo.dev_operatorversions.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ spec:
7373
displayName:
7474
description: DisplayName can be used by UIs.
7575
type: string
76+
enum:
77+
description: Defines a list of allowed values. If Default is set and Enum is not nil, the value must be in this list as well
78+
items:
79+
type: string
80+
type: array
7681
immutable:
7782
description: Specifies if the parameter can be changed after the initial installation of the operator
7883
type: boolean

pkg/apis/kudo/v1beta1/operatorversion_types.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,16 @@ const (
7575
// StringValueType is used for parameter values that are provided as a string.
7676
StringValueType ParameterType = "string"
7777

78-
// ArrayValueType is used for parameter values that described an array of values.
78+
// IntegerValueType is used for parameter values that describe an integral number without any fractional part
79+
IntegerValueType ParameterType = "integer"
80+
81+
// NumberValueType is used for parameter values that describe any numeric value, with or without a fractional part
82+
NumberValueType ParameterType = "number"
83+
84+
// BooleanValueType is used for parameter values that are "true" or "false"
85+
BooleanValueType ParameterType = "boolean"
86+
87+
// ArrayValueType is used for parameter values that describe an array of values.
7988
ArrayValueType ParameterType = "array"
8089

8190
// MapValueType is used for parameter values that describe a mapping type.

pkg/apis/kudo/v1beta1/parameter_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,7 @@ type Parameter struct {
3030

3131
// Specifies if the parameter can be changed after the initial installation of the operator
3232
Immutable *bool `json:"immutable,omitempty"`
33+
34+
// Defines a list of allowed values. If Default is set and Enum is not nil, the value must be in this list as well
35+
Enum *[]string `json:"enum,omitempty"`
3336
}

pkg/apis/kudo/v1beta1/parameter_types_helpers.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package v1beta1
22

3+
import (
4+
"fmt"
5+
"reflect"
6+
"strconv"
7+
8+
"sigs.k8s.io/yaml"
9+
)
10+
311
func (p *Parameter) IsImmutable() bool {
412
return p.Immutable != nil && *p.Immutable
513
}
@@ -8,10 +16,173 @@ func (p *Parameter) IsRequired() bool {
816
return p.Required != nil && *p.Required
917
}
1018

19+
func (p *Parameter) IsEnum() bool {
20+
return p.Enum != nil
21+
}
22+
1123
func (p *Parameter) HasDefault() bool {
1224
return p.Default != nil
1325
}
1426

27+
func (p *Parameter) EnumValues() []string {
28+
if p.IsEnum() {
29+
return *p.Enum
30+
}
31+
return []string{}
32+
}
33+
34+
func (p *Parameter) ValidateDefault() error {
35+
if err := ValidateParameterValueForType(p.Type, p.Default); err != nil {
36+
return fmt.Errorf("parameter %q has an invalid default value: %v", p.Name, err)
37+
}
38+
if p.IsEnum() {
39+
if err := ValidateParameterValueForEnum(p.EnumValues(), p.Default); err != nil {
40+
return fmt.Errorf("parameter %q has an invalid default value: %v", p.Name, err)
41+
}
42+
}
43+
return nil
44+
}
45+
46+
// ValidateValue ensures that a the given value is valid for this parameter
47+
func (p *Parameter) ValidateValue(pValue string) error {
48+
if p.IsRequired() && !p.HasDefault() && pValue == "" {
49+
return fmt.Errorf("parameter %q is required but has no value set", p.Name)
50+
}
51+
52+
if pValue == "" {
53+
return nil
54+
}
55+
56+
if err := ValidateParameterValueForType(p.Type, pValue); err != nil {
57+
return fmt.Errorf("parameter %q has an invalid value %q: %v", p.Name, pValue, err)
58+
}
59+
if p.IsEnum() {
60+
if err := ValidateParameterValueForEnum(p.EnumValues(), pValue); err != nil {
61+
return fmt.Errorf("parameter %q has an invalid value %q: %v", p.Name, pValue, err)
62+
}
63+
}
64+
return nil
65+
}
66+
67+
func ValidateParameterValueForType(pType ParameterType, pValue interface{}) error {
68+
switch pType {
69+
case StringValueType:
70+
_, ok := pValue.(string)
71+
if !ok {
72+
return fmt.Errorf("type is %q but format is invalid: %s", pType, pValue)
73+
}
74+
case IntegerValueType:
75+
return validateIntegerType(pValue)
76+
case NumberValueType:
77+
return validateNumberType(pValue)
78+
case BooleanValueType:
79+
return validateBooleanType(pValue)
80+
case ArrayValueType:
81+
return validateArrayType(pValue)
82+
case MapValueType:
83+
return validateMapType(pValue)
84+
}
85+
return nil
86+
}
87+
88+
func validateIntegerType(pValue interface{}) error {
89+
switch v := pValue.(type) {
90+
case int, int8, int16, int32, int64:
91+
return nil
92+
case uint, uint8, uint16, uint32, uint64:
93+
return nil
94+
case string:
95+
_, err := strconv.ParseInt(v, 10, 64)
96+
if err != nil {
97+
return fmt.Errorf("type is %q but format of %q is invalid: %v", IntegerValueType, pValue, err)
98+
}
99+
default:
100+
return fmt.Errorf("type is %q but format of %s is invalid", IntegerValueType, pValue)
101+
}
102+
return nil
103+
}
104+
105+
func validateNumberType(pValue interface{}) error {
106+
switch v := pValue.(type) {
107+
case int, int8, int16, int32, int64:
108+
return nil
109+
case uint, uint8, uint16, uint32, uint64:
110+
return nil
111+
case float32, float64:
112+
return nil
113+
case string:
114+
_, err := strconv.ParseFloat(v, 64)
115+
if err != nil {
116+
return fmt.Errorf("type is %q but format of %q is invalid: %v", NumberValueType, pValue, err)
117+
}
118+
default:
119+
return fmt.Errorf("type is %q but format of %s is invalid", NumberValueType, v)
120+
}
121+
return nil
122+
}
123+
124+
func validateBooleanType(pValue interface{}) error {
125+
switch v := pValue.(type) {
126+
case bool:
127+
return nil
128+
case string:
129+
_, err := strconv.ParseBool(v)
130+
if err != nil {
131+
return fmt.Errorf("type is %q but format of %q is invalid: %v", BooleanValueType, pValue, err)
132+
}
133+
default:
134+
return fmt.Errorf("type is %q but format of %s is invalid", BooleanValueType, pValue)
135+
}
136+
return nil
137+
}
138+
139+
func validateArrayType(pValue interface{}) error {
140+
t := reflect.TypeOf(pValue)
141+
switch t.Kind() {
142+
case reflect.Slice, reflect.Array:
143+
return nil
144+
case reflect.String:
145+
str, _ := pValue.(string) // We know here this is a string
146+
147+
var result []interface{}
148+
if err := yaml.Unmarshal([]byte(str), &result); err != nil {
149+
return fmt.Errorf("type is %q, but format of %s is invalid", ArrayValueType, pValue)
150+
}
151+
152+
return nil
153+
default:
154+
return fmt.Errorf("type is %q but value %s is not an array", ArrayValueType, pValue)
155+
}
156+
}
157+
158+
func validateMapType(pValue interface{}) error {
159+
t := reflect.TypeOf(pValue)
160+
switch t.Kind() {
161+
case reflect.Map, reflect.Struct:
162+
return nil
163+
case reflect.String:
164+
str, _ := pValue.(string) // We know here this is a string
165+
166+
var result map[string]interface{}
167+
if err := yaml.Unmarshal([]byte(str), &result); err != nil {
168+
return fmt.Errorf("type is %q, but format of %s is invalid", MapValueType, pValue)
169+
}
170+
171+
return nil
172+
default:
173+
return fmt.Errorf("type is %q but value %s is not a map", MapValueType, pValue)
174+
}
175+
}
176+
177+
func ValidateParameterValueForEnum(enumValues []string, pValue interface{}) error {
178+
for _, eValue := range enumValues {
179+
if pValue == eValue {
180+
return nil
181+
}
182+
}
183+
return fmt.Errorf("value is %q, but only allowed values are %v", pValue, enumValues)
184+
}
185+
15186
// GetChangedParamDefs returns a list of parameters from ov2 that changed based on the given compare function between ov1 and ov2
16187
func GetChangedParamDefs(p1, p2 []Parameter, isEqual func(p1, p2 Parameter) bool) []Parameter {
17188
changedParams := []Parameter{}

pkg/apis/kudo/v1beta1/parameter_types_helpers_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,92 @@ func TestGetAddedRemovedParamDefs(t *testing.T) {
137137
})
138138
}
139139
}
140+
141+
func TestValidateType(t *testing.T) {
142+
143+
tests := []struct {
144+
name string
145+
pValue interface{}
146+
pType ParameterType
147+
expectedErr bool
148+
}{
149+
{
150+
name: "simple int",
151+
pValue: 23,
152+
pType: IntegerValueType,
153+
},
154+
{
155+
name: "int8",
156+
pValue: int8(23),
157+
pType: IntegerValueType,
158+
},
159+
{
160+
name: "int64",
161+
pValue: int64(23),
162+
pType: IntegerValueType,
163+
},
164+
{
165+
name: "intAsString",
166+
pValue: "42",
167+
pType: IntegerValueType,
168+
},
169+
{
170+
name: "float32",
171+
pValue: float32(3.14),
172+
pType: NumberValueType,
173+
},
174+
{
175+
name: "float64",
176+
pValue: float64(3.1415),
177+
pType: NumberValueType,
178+
},
179+
{
180+
name: "floatAsString",
181+
pValue: "3.1415",
182+
pType: NumberValueType,
183+
},
184+
{
185+
name: "bool",
186+
pValue: true,
187+
pType: BooleanValueType,
188+
},
189+
{
190+
name: "boolAsString",
191+
pValue: "true",
192+
pType: BooleanValueType,
193+
},
194+
{
195+
name: "array",
196+
pValue: []string{"oneString", "twoString"},
197+
pType: ArrayValueType,
198+
},
199+
{
200+
name: "arrayAsString",
201+
pValue: `[ "oneString", "twoString" ]`,
202+
pType: ArrayValueType,
203+
},
204+
{
205+
name: "map",
206+
pValue: map[string]string{"oneString": "oneValue", "twoString": "twoValue"},
207+
pType: MapValueType,
208+
},
209+
{
210+
name: "mapAsString",
211+
pValue: `{ "oneString": "oneValue", "twoString": "twoValue" }`,
212+
pType: MapValueType,
213+
},
214+
}
215+
216+
for _, tt := range tests {
217+
tt := tt
218+
t.Run(tt.name, func(t *testing.T) {
219+
err := ValidateParameterValueForType(tt.pType, tt.pValue)
220+
221+
if tt.expectedErr {
222+
assert.Error(t, err)
223+
} else {
224+
assert.NoError(t, err)
225+
}
226+
})
227+
}
228+
}

pkg/apis/kudo/v1beta1/zz_generated.deepcopy.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/kudoctl/cmd/testdata/deploy-kudo-ns.yaml.golden

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ spec:
142142
displayName:
143143
description: DisplayName can be used by UIs.
144144
type: string
145+
enum:
146+
description: Defines a list of allowed values. If Default is set and Enum is not nil, the value must be in this list as well
147+
items:
148+
type: string
149+
type: array
145150
immutable:
146151
description: Specifies if the parameter can be changed after the initial installation of the operator
147152
type: boolean

pkg/kudoctl/cmd/testdata/deploy-kudo-sa.yaml.golden

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ spec:
142142
displayName:
143143
description: DisplayName can be used by UIs.
144144
type: string
145+
enum:
146+
description: Defines a list of allowed values. If Default is set and Enum is not nil, the value must be in this list as well
147+
items:
148+
type: string
149+
type: array
145150
immutable:
146151
description: Specifies if the parameter can be changed after the initial installation of the operator
147152
type: boolean

pkg/kudoctl/cmd/testdata/deploy-kudo.json.golden

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,13 @@
196196
"description": "DisplayName can be used by UIs.",
197197
"type": "string"
198198
},
199+
"enum": {
200+
"description": "Defines a list of allowed values. If Default is set and Enum is not nil, the value must be in this list as well",
201+
"type": "array",
202+
"items": {
203+
"type": "string"
204+
}
205+
},
199206
"immutable": {
200207
"description": "Specifies if the parameter can be changed after the initial installation of the operator",
201208
"type": "boolean"

pkg/kudoctl/cmd/testdata/deploy-kudo.yaml.golden

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ spec:
142142
displayName:
143143
description: DisplayName can be used by UIs.
144144
type: string
145+
enum:
146+
description: Defines a list of allowed values. If Default is set and Enum is not nil, the value must be in this list as well
147+
items:
148+
type: string
149+
type: array
145150
immutable:
146151
description: Specifies if the parameter can be changed after the initial installation of the operator
147152
type: boolean

0 commit comments

Comments
 (0)