diff --git a/pkg/cloudcommon/validators/errors.go b/pkg/cloudcommon/validators/errors.go index 994561c217..5cb18b8fcd 100644 --- a/pkg/cloudcommon/validators/errors.go +++ b/pkg/cloudcommon/validators/errors.go @@ -98,6 +98,18 @@ func newInvalidChoiceError(key string, choices choices.Choices, choice string) e return newError(ERR_INVALID_CHOICE, "invalid %q, want %s, got %s", key, choices, choice) } +func newInvalidIntChoiceError(key string, choices []int64, choice int64) error { + wantS := "" + for i, c := range choices { + if i > 0 { + wantS += ", " + } + wantS += fmt.Sprintf("%d", c) + } + gotS := fmt.Sprintf("%d", choice) + return newError(ERR_INVALID_CHOICE, "invalid %q, want %s, got %s", key, wantS, gotS) +} + func newStringTooShortError(key string, got, want int) error { return newError(ERR_INVALID_LENGTH, "%q too short, got %d, min %s", key, got, want) } diff --git a/pkg/cloudcommon/validators/validators.go b/pkg/cloudcommon/validators/validators.go index d3c997675f..0ecf5a0946 100644 --- a/pkg/cloudcommon/validators/validators.go +++ b/pkg/cloudcommon/validators/validators.go @@ -158,6 +158,60 @@ func (v *ValidatorIPv4Prefix) Validate(data *jsonutils.JSONDict) error { return nil } +type ValidatorIntChoices struct { + Validator + choices []int64 + + Value int64 +} + +func NewIntChoicesValidator(key string, choices []int64) *ValidatorIntChoices { + v := &ValidatorIntChoices{ + Validator: Validator{Key: key}, + choices: choices, + } + v.SetParent(v) + return v +} + +func (v *ValidatorIntChoices) has(i int64) bool { + for _, c := range v.choices { + if c == i { + return true + } + } + return false +} + +func (v *ValidatorIntChoices) Default(i int64) IValidator { + if v.has(i) { + v.Validator.Default(i) + return v + } + panic("invalid default for " + v.Key) +} + +func (v *ValidatorIntChoices) getValue() interface{} { + return v.Value +} + +func (v *ValidatorIntChoices) Validate(data *jsonutils.JSONDict) error { + if err, isSet := v.Validator.validateEx(data); err != nil || !isSet { + return err + } + i, err := v.value.Int() + if err != nil { + return newGeneralError(v.Key, err) + } + if !v.has(i) { + return newInvalidIntChoiceError(v.Key, v.choices, i) + } + // in case it's stringified from v.value + data.Set(v.Key, jsonutils.NewInt(i)) + v.Value = i + return nil +} + type ValidatorStringChoices struct { Validator Choices choices.Choices diff --git a/pkg/cloudcommon/validators/validators_test.go b/pkg/cloudcommon/validators/validators_test.go index 7246b14141..9ca18d24d4 100644 --- a/pkg/cloudcommon/validators/validators_test.go +++ b/pkg/cloudcommon/validators/validators_test.go @@ -161,6 +161,73 @@ func TestStringChoicesValidator(t *testing.T) { } } +func TestIntChoicesValidator(t *testing.T) { + choices := []int64{-1, 0, 100} + cases := []*C{ + { + Name: "missing non-optional", + In: `{}`, + Out: `{}`, + Optional: false, + Err: ERR_MISSING_KEY, + ValueWant: int64(0), + }, + { + Name: "missing optional", + In: `{}`, + Out: `{}`, + Optional: true, + ValueWant: int64(0), + }, + { + Name: "missing with default", + In: `{}`, + Out: `{s: -1}`, + Default: int64(-1), + ValueWant: int64(-1), + }, + { + Name: "stringified", + In: `{"s": "100"}`, + Out: `{s: 100}`, + ValueWant: int64(100), + }, + { + Name: "stringified invalid choice", + In: `{"s": "101"}`, + Out: `{"s": "101"}`, + Err: ERR_INVALID_CHOICE, + ValueWant: int64(0), + }, + { + Name: "good choice", + In: `{"s": 0}`, + Out: `{"s": 0}`, + ValueWant: int64(0), + }, + { + Name: "bad choice", + In: `{"s": 101}`, + Out: `{"s": 101}`, + Err: ERR_INVALID_CHOICE, + ValueWant: int64(0), + }, + } + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + v := NewIntChoicesValidator("s", choices) + if c.Default != nil { + s := c.Default.(int64) + v.Default(s) + } + if c.Optional { + v.Optional(true) + } + testS(t, v, c) + }) + } +} + func TestStringMultiChoicesValidator(t *testing.T) { type MultiChoicesC struct { *C