update: refactor config
- Explicitly specify all of the valid options for Update - Seperate the config from File() and Units() - Add YAML tags for the fields
This commit is contained in:
66
config/config.go
Normal file
66
config/config.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsZero returns whether or not the parameter is the zero value for its type.
|
||||
// If the parameter is a struct, only the exported fields are considered.
|
||||
func IsZero(c interface{}) bool {
|
||||
return isZero(reflect.ValueOf(c))
|
||||
}
|
||||
|
||||
// AssertValid checks the fields in the structure and makes sure that they
|
||||
// contain valid values as specified by the 'valid' flag. Empty fields are
|
||||
// implicitly valid.
|
||||
func AssertValid(c interface{}) error {
|
||||
ct := reflect.TypeOf(c)
|
||||
cv := reflect.ValueOf(c)
|
||||
for i := 0; i < ct.NumField(); i++ {
|
||||
ft := ct.Field(i)
|
||||
if !isFieldExported(ft) {
|
||||
continue
|
||||
}
|
||||
|
||||
valid := ft.Tag.Get("valid")
|
||||
val := cv.Field(i)
|
||||
if !isValid(val, valid) {
|
||||
return fmt.Errorf("invalid value \"%v\" for option %q (valid options: %q)", val.Interface(), ft.Name, valid)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
vt := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if isFieldExported(vt.Field(i)) && !isZero(v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return v.Interface() == reflect.Zero(v.Type()).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
func isFieldExported(f reflect.StructField) bool {
|
||||
return f.PkgPath == ""
|
||||
}
|
||||
|
||||
func isValid(v reflect.Value, valid string) bool {
|
||||
if valid == "" || isZero(v) {
|
||||
return true
|
||||
}
|
||||
vs := fmt.Sprintf("%v", v.Interface())
|
||||
for _, valid := range strings.Split(valid, ",") {
|
||||
if vs == valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
63
config/config_test.go
Normal file
63
config/config_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsZero(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
c interface{}
|
||||
empty bool
|
||||
}{
|
||||
{struct{}{}, true},
|
||||
{struct{ a, b string }{}, true},
|
||||
{struct{ A, b string }{}, true},
|
||||
{struct{ A, B string }{}, true},
|
||||
{struct{ A string }{A: "hello"}, false},
|
||||
{struct{ A int }{}, true},
|
||||
{struct{ A int }{A: 1}, false},
|
||||
} {
|
||||
if empty := IsZero(tt.c); tt.empty != empty {
|
||||
t.Errorf("bad result (%q): want %q, got %q", tt.c, tt.empty, empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssertValid(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
c interface{}
|
||||
err error
|
||||
}{
|
||||
{struct{}{}, nil},
|
||||
{struct {
|
||||
A, b string `valid:"1,2"`
|
||||
}{}, nil},
|
||||
{struct {
|
||||
A, b string `valid:"1,2"`
|
||||
}{A: "1", b: "2"}, nil},
|
||||
{struct {
|
||||
A, b string `valid:"1,2"`
|
||||
}{A: "1", b: "hello"}, nil},
|
||||
{struct {
|
||||
A, b string `valid:"1,2"`
|
||||
}{A: "hello", b: "2"}, errors.New("invalid value \"hello\" for option \"A\" (valid options: \"1,2\")")},
|
||||
{struct {
|
||||
A, b int `valid:"1,2"`
|
||||
}{}, nil},
|
||||
{struct {
|
||||
A, b int `valid:"1,2"`
|
||||
}{A: 1, b: 2}, nil},
|
||||
{struct {
|
||||
A, b int `valid:"1,2"`
|
||||
}{A: 1, b: 9}, nil},
|
||||
{struct {
|
||||
A, b int `valid:"1,2"`
|
||||
}{A: 9, b: 2}, errors.New("invalid value \"9\" for option \"A\" (valid options: \"1,2\")")},
|
||||
} {
|
||||
if err := AssertValid(tt.c); !reflect.DeepEqual(tt.err, err) {
|
||||
t.Errorf("bad result (%q): want %q, got %q", tt.c, tt.err, err)
|
||||
}
|
||||
}
|
||||
}
|
3
config/etc_hosts.go
Normal file
3
config/etc_hosts.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package config
|
||||
|
||||
type EtcHosts string
|
7
config/update.go
Normal file
7
config/update.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
type Update struct {
|
||||
RebootStrategy string `yaml:"reboot-strategy" env:"REBOOT_STRATEGY" valid:"best-effort,etcd-lock,reboot,off"`
|
||||
Group string `yaml:"group" env:"GROUP"`
|
||||
Server string `yaml:"server" env:"SERVER"`
|
||||
}
|
Reference in New Issue
Block a user