From 7137b23d04c632c22097cebe11d067f3b18b4920 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Sat, 16 Dec 2017 01:26:07 -0700 Subject: [PATCH] Conditions framework --- condition/condition.go | 149 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 condition/condition.go diff --git a/condition/condition.go b/condition/condition.go new file mode 100644 index 00000000..85a5ef1c --- /dev/null +++ b/condition/condition.go @@ -0,0 +1,149 @@ +package condition + +import ( + "reflect" + "time" + + "github.com/pkg/errors" + "github.com/rancher/norman/controller" + "k8s.io/apimachinery/pkg/runtime" +) + +type Cond string + +func (c Cond) True(obj runtime.Object) { + setStatus(obj, string(c), "True") +} + +func (c Cond) IsTrue(obj runtime.Object) bool { + return getStatus(obj, string(c)) == "True" +} + +func (c Cond) False(obj runtime.Object) { + setStatus(obj, string(c), "False") +} + +func (c Cond) IsFalse(obj runtime.Object) bool { + return getStatus(obj, string(c)) == "False" +} + +func (c Cond) Unknown(obj runtime.Object) { + setStatus(obj, string(c), "Unknown") +} + +func (c Cond) IsUnknown(obj runtime.Object) bool { + return getStatus(obj, string(c)) == "Unknown" +} + +func (c Cond) Reason(obj runtime.Object, reason string) { + cond := findOrCreateCond(obj, string(c)) + getFieldValue(cond, "Reason").SetString(reason) + touchTS(cond) +} + +func (c Cond) GetReason(obj runtime.Object) string { + cond := findOrCreateCond(obj, string(c)) + return getFieldValue(cond, "Reason").String() +} + +func (c Cond) Once(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) { + if c.IsFalse(obj) { + return obj, &controller.ForgetError{ + Err: errors.New(c.GetReason(obj)), + } + } + + if c.IsTrue(obj) { + return obj, nil + } + + c.Unknown(obj) + newObj, err := f() + if newObj != nil { + obj = newObj + } + + if err != nil { + c.False(obj) + c.Reason(obj, err.Error()) + return obj, err + } + c.True(obj) + return obj, nil +} + +func (c Cond) Do(obj runtime.Object, f func() error) error { + c.Unknown(obj) + if err := f(); err != nil { + c.False(obj) + c.Reason(obj, err.Error()) + return err + } + c.True(obj) + return nil +} + +func touchTS(value reflect.Value) { + now := time.Now().Format(time.RFC3339) + getFieldValue(value, "LastUpdateTime").SetString(now) +} + +func getStatus(obj interface{}, condName string) string { + cond := findOrCreateCond(obj, condName) + return getFieldValue(cond, "Status").String() +} + +func setStatus(obj interface{}, condName, status string) { + cond := findOrCreateCond(obj, condName) + getFieldValue(cond, "Status").SetString(status) + touchTS(cond) +} + +func findOrCreateCond(obj interface{}, condName string) reflect.Value { + condSlice := getValue(obj, "Status", "Conditions") + cond := findCond(condSlice, condName) + if cond != nil { + return *cond + } + + newCond := reflect.New(condSlice.Type().Elem()).Elem() + newCond.FieldByName("Type").SetString(condName) + newCond.FieldByName("Status").SetString("Unknown") + condSlice.Set(reflect.Append(condSlice, newCond)) + return newCond +} + +func findCond(val reflect.Value, name string) *reflect.Value { + for i := 0; i < val.Len(); i++ { + cond := val.Index(i) + typeVal := getFieldValue(cond, "Type") + if typeVal.String() == name { + return &cond + } + } + + return nil +} + +func getValue(obj interface{}, name ...string) reflect.Value { + v := reflect.ValueOf(obj) + t := v.Type() + if t.Kind() == reflect.Ptr { + v = v.Elem() + t = v.Type() + } + + field := v.FieldByName(name[0]) + if len(name) == 1 { + return field + } + return getFieldValue(field, name[1:]...) +} + +func getFieldValue(v reflect.Value, name ...string) reflect.Value { + field := v.FieldByName(name[0]) + if len(name) == 1 { + return field + } + return getFieldValue(field, name[1:]...) +}