2017-12-16 08:26:07 +00:00
|
|
|
package condition
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
2018-08-17 01:06:52 +00:00
|
|
|
"regexp"
|
2017-12-16 08:26:07 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/rancher/norman/controller"
|
2018-01-12 10:12:30 +00:00
|
|
|
err2 "k8s.io/apimachinery/pkg/api/errors"
|
2017-12-16 08:26:07 +00:00
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Cond string
|
|
|
|
|
2018-08-17 01:06:52 +00:00
|
|
|
var temfileRegexp = regexp.MustCompile("/tmp/[-_a-zA-Z0-9]+")
|
|
|
|
|
2017-12-16 08:26:07 +00:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2018-01-17 01:03:48 +00:00
|
|
|
func (c Cond) LastUpdated(obj runtime.Object, ts string) {
|
|
|
|
setTS(obj, string(c), ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cond) GetLastUpdated(obj runtime.Object) string {
|
|
|
|
return getTS(obj, string(c))
|
|
|
|
}
|
|
|
|
|
2017-12-16 08:26:07 +00:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2018-01-17 01:03:48 +00:00
|
|
|
func (c Cond) GetStatus(obj runtime.Object) string {
|
|
|
|
return getStatus(obj, string(c))
|
|
|
|
}
|
|
|
|
|
2017-12-16 08:26:07 +00:00
|
|
|
func (c Cond) Unknown(obj runtime.Object) {
|
|
|
|
setStatus(obj, string(c), "Unknown")
|
|
|
|
}
|
|
|
|
|
2018-01-17 01:03:48 +00:00
|
|
|
func (c Cond) CreateUnknownIfNotExists(obj runtime.Object) {
|
|
|
|
condSlice := getValue(obj, "Status", "Conditions")
|
|
|
|
cond := findCond(condSlice, string(c))
|
|
|
|
if cond == nil {
|
|
|
|
c.Unknown(obj)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-16 08:26:07 +00:00
|
|
|
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)
|
2017-12-27 16:24:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-22 23:43:22 +00:00
|
|
|
func (c Cond) SetMessageIfBlank(obj runtime.Object, message string) {
|
|
|
|
if c.GetMessage(obj) == "" {
|
|
|
|
c.Message(obj, message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-27 16:24:07 +00:00
|
|
|
func (c Cond) Message(obj runtime.Object, message string) {
|
|
|
|
cond := findOrCreateCond(obj, string(c))
|
2018-01-17 01:03:48 +00:00
|
|
|
setValue(cond, "Message", message)
|
2017-12-27 16:24:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 10:12:30 +00:00
|
|
|
func (c Cond) GetMessage(obj runtime.Object) string {
|
2018-01-22 23:43:22 +00:00
|
|
|
cond := findOrNotCreateCond(obj, string(c))
|
|
|
|
if cond == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return getFieldValue(*cond, "Message").String()
|
2018-01-12 10:12:30 +00:00
|
|
|
}
|
|
|
|
|
2017-12-27 20:58:02 +00:00
|
|
|
func (c Cond) ReasonAndMessageFromError(obj runtime.Object, err error) {
|
2018-01-12 10:12:30 +00:00
|
|
|
if err2.IsConflict(err) {
|
|
|
|
return
|
|
|
|
}
|
2017-12-27 16:24:07 +00:00
|
|
|
cond := findOrCreateCond(obj, string(c))
|
2018-01-16 19:52:12 +00:00
|
|
|
setValue(cond, "Message", err.Error())
|
2018-10-02 17:44:42 +00:00
|
|
|
switch ce := err.(type) {
|
|
|
|
case *conditionError:
|
2018-01-16 19:52:12 +00:00
|
|
|
setValue(cond, "Reason", ce.reason)
|
2018-10-02 17:44:42 +00:00
|
|
|
case *controller.ForgetError:
|
|
|
|
if ce.Reason != "" {
|
|
|
|
setValue(cond, "Reason", ce.Reason)
|
|
|
|
} else {
|
|
|
|
setValue(cond, "Reason", "Error")
|
|
|
|
}
|
|
|
|
default:
|
2018-01-16 19:52:12 +00:00
|
|
|
setValue(cond, "Reason", "Error")
|
2017-12-27 16:24:07 +00:00
|
|
|
}
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cond) GetReason(obj runtime.Object) string {
|
2018-01-22 23:43:22 +00:00
|
|
|
cond := findOrNotCreateCond(obj, string(c))
|
|
|
|
if cond == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return getFieldValue(*cond, "Reason").String()
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-17 01:03:48 +00:00
|
|
|
return c.DoUntilTrue(obj, f)
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 19:32:41 +00:00
|
|
|
func (c Cond) DoUntilTrue(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
|
|
|
|
if c.IsTrue(obj) {
|
|
|
|
return obj, nil
|
|
|
|
}
|
|
|
|
|
2018-01-17 01:03:48 +00:00
|
|
|
return c.do(obj, f)
|
2018-01-08 19:32:41 +00:00
|
|
|
}
|
|
|
|
|
2017-12-27 18:12:30 +00:00
|
|
|
func (c Cond) Do(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
|
2018-01-08 19:32:41 +00:00
|
|
|
return c.do(obj, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cond) do(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
|
2018-01-17 01:03:48 +00:00
|
|
|
status := c.GetStatus(obj)
|
|
|
|
ts := c.GetLastUpdated(obj)
|
|
|
|
reason := c.GetReason(obj)
|
|
|
|
message := c.GetMessage(obj)
|
|
|
|
|
|
|
|
obj, err := c.doInternal(obj, f)
|
|
|
|
|
|
|
|
// This is to prevent non stop flapping of states and update
|
|
|
|
if status == c.GetStatus(obj) &&
|
2018-08-17 01:06:52 +00:00
|
|
|
reason == c.GetReason(obj) {
|
|
|
|
if message != c.GetMessage(obj) {
|
|
|
|
replaced := temfileRegexp.ReplaceAllString(c.GetMessage(obj), "file_path_redacted")
|
|
|
|
c.Message(obj, replaced)
|
|
|
|
}
|
|
|
|
if message == c.GetMessage(obj) {
|
|
|
|
c.LastUpdated(obj, ts)
|
|
|
|
}
|
2018-01-17 01:03:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return obj, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cond) doInternal(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
|
|
|
|
if !c.IsFalse(obj) {
|
|
|
|
c.Unknown(obj)
|
|
|
|
}
|
|
|
|
|
2017-12-18 22:16:28 +00:00
|
|
|
newObj, err := f()
|
2018-01-16 05:08:13 +00:00
|
|
|
if newObj != nil && !reflect.ValueOf(newObj).IsNil() {
|
2017-12-18 22:16:28 +00:00
|
|
|
obj = newObj
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2018-01-17 22:42:31 +00:00
|
|
|
if _, ok := err.(*controller.ForgetError); ok {
|
2018-02-06 04:59:30 +00:00
|
|
|
if c.GetMessage(obj) == "" {
|
|
|
|
c.ReasonAndMessageFromError(obj, err)
|
2018-01-17 22:42:31 +00:00
|
|
|
}
|
2018-02-06 04:59:30 +00:00
|
|
|
return obj, err
|
2018-01-17 01:03:48 +00:00
|
|
|
}
|
2018-02-06 04:59:30 +00:00
|
|
|
c.False(obj)
|
2017-12-27 20:58:02 +00:00
|
|
|
c.ReasonAndMessageFromError(obj, err)
|
2017-12-27 18:12:30 +00:00
|
|
|
return obj, err
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
c.True(obj)
|
2017-12-27 16:24:07 +00:00
|
|
|
c.Reason(obj, "")
|
|
|
|
c.Message(obj, "")
|
2017-12-27 18:12:30 +00:00
|
|
|
return obj, nil
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func touchTS(value reflect.Value) {
|
|
|
|
now := time.Now().Format(time.RFC3339)
|
|
|
|
getFieldValue(value, "LastUpdateTime").SetString(now)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getStatus(obj interface{}, condName string) string {
|
2018-01-22 23:43:22 +00:00
|
|
|
cond := findOrNotCreateCond(obj, condName)
|
|
|
|
if cond == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return getFieldValue(*cond, "Status").String()
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 01:03:48 +00:00
|
|
|
func setTS(obj interface{}, condName, ts string) {
|
|
|
|
cond := findOrCreateCond(obj, condName)
|
|
|
|
getFieldValue(cond, "LastUpdateTime").SetString(ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTS(obj interface{}, condName string) string {
|
2018-01-22 23:43:22 +00:00
|
|
|
cond := findOrNotCreateCond(obj, condName)
|
|
|
|
if cond == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return getFieldValue(*cond, "LastUpdateTime").String()
|
2018-01-17 01:03:48 +00:00
|
|
|
}
|
|
|
|
|
2017-12-16 08:26:07 +00:00
|
|
|
func setStatus(obj interface{}, condName, status string) {
|
|
|
|
cond := findOrCreateCond(obj, condName)
|
2018-01-16 19:52:12 +00:00
|
|
|
setValue(cond, "Status", status)
|
|
|
|
}
|
|
|
|
|
|
|
|
func setValue(cond reflect.Value, fieldName, newValue string) {
|
|
|
|
value := getFieldValue(cond, fieldName)
|
|
|
|
if value.String() != newValue {
|
|
|
|
value.SetString(newValue)
|
|
|
|
touchTS(cond)
|
|
|
|
}
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-22 23:43:22 +00:00
|
|
|
func findOrNotCreateCond(obj interface{}, condName string) *reflect.Value {
|
|
|
|
condSlice := getValue(obj, "Status", "Conditions")
|
|
|
|
return findCond(condSlice, condName)
|
|
|
|
}
|
|
|
|
|
2017-12-16 08:26:07 +00:00
|
|
|
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))
|
2018-02-03 06:42:05 +00:00
|
|
|
return *findCond(condSlice, condName)
|
2017-12-16 08:26:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2018-01-22 22:44:51 +00:00
|
|
|
if obj == nil {
|
|
|
|
return reflect.Value{}
|
|
|
|
}
|
2017-12-16 08:26:07 +00:00
|
|
|
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:]...)
|
|
|
|
}
|
2017-12-27 16:24:07 +00:00
|
|
|
|
|
|
|
func Error(reason string, err error) error {
|
|
|
|
return &conditionError{
|
|
|
|
reason: reason,
|
|
|
|
message: err.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type conditionError struct {
|
|
|
|
reason string
|
|
|
|
message string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *conditionError) Error() string {
|
|
|
|
return e.message
|
|
|
|
}
|