k3s/vendor/github.com/rancher/norman/condition/condition.go

363 lines
8.7 KiB
Go

package condition
import (
"reflect"
"regexp"
"time"
"github.com/pkg/errors"
"github.com/rancher/norman/controller"
"github.com/rancher/norman/objectclient"
"k8s.io/api/core/v1"
err2 "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type Cond string
var temfileRegexp = regexp.MustCompile("/tmp/[-_a-zA-Z0-9]+")
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) LastUpdated(obj runtime.Object, ts string) {
setTS(obj, string(c), ts)
}
func (c Cond) GetLastUpdated(obj runtime.Object) string {
return getTS(obj, string(c))
}
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) GetStatus(obj runtime.Object) string {
return getStatus(obj, string(c))
}
func (c Cond) SetStatus(obj runtime.Object, status string) {
setStatus(obj, string(c), status)
}
func (c Cond) Unknown(obj runtime.Object) {
setStatus(obj, string(c), "Unknown")
}
func (c Cond) CreateUnknownIfNotExists(obj runtime.Object) {
condSlice := getValue(obj, "Status", "Conditions")
cond := findCond(condSlice, string(c))
if cond == nil {
c.Unknown(obj)
}
}
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)
}
func (c Cond) SetMessageIfBlank(obj runtime.Object, message string) {
if c.GetMessage(obj) == "" {
c.Message(obj, message)
}
}
func (c Cond) Message(obj runtime.Object, message string) {
cond := findOrCreateCond(obj, string(c))
setValue(cond, "Message", message)
}
func (c Cond) GetMessage(obj runtime.Object) string {
cond := findOrNotCreateCond(obj, string(c))
if cond == nil {
return ""
}
return getFieldValue(*cond, "Message").String()
}
func (c Cond) ReasonAndMessageFromError(obj runtime.Object, err error) {
if err2.IsConflict(err) {
return
}
cond := findOrCreateCond(obj, string(c))
setValue(cond, "Message", err.Error())
switch ce := err.(type) {
case *conditionError:
setValue(cond, "Reason", ce.reason)
case *controller.ForgetError:
if ce.Reason != "" {
setValue(cond, "Reason", ce.Reason)
} else {
setValue(cond, "Reason", "Error")
}
default:
setValue(cond, "Reason", "Error")
}
}
func (c Cond) GetReason(obj runtime.Object) string {
cond := findOrNotCreateCond(obj, string(c))
if cond == nil {
return ""
}
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)),
}
}
return c.DoUntilTrue(obj, f)
}
func (c Cond) DoUntilTrue(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
if c.IsTrue(obj) {
return obj, nil
}
return c.do(obj, f)
}
func (c Cond) Do(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
return c.do(obj, f)
}
type ObjectClientGetter interface {
ObjectClient() *objectclient.ObjectClient
}
func (c Cond) Track(obj runtime.Object, client ObjectClientGetter, f func() (runtime.Object, error)) (runtime.Object, error) {
obj = obj.DeepCopyObject()
retObj, changed, err := c.do2(false, obj, f)
if !changed {
return retObj, err
}
c.SetStatus(retObj, c.GetStatus(obj))
c.LastUpdated(retObj, c.GetLastUpdated(obj))
c.Reason(retObj, c.GetReason(obj))
c.Message(retObj, c.GetMessage(obj))
if obj, ok := retObj.(metav1.Object); ok {
updated, uerr := client.ObjectClient().Update(obj.GetName(), retObj)
if uerr == nil {
return updated, err
}
}
return retObj, err
}
func (c Cond) do(obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
obj, _, err := c.do2(true, obj, f)
return obj, err
}
func (c Cond) do2(setReturned bool, obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, bool, error) {
status := c.GetStatus(obj)
ts := c.GetLastUpdated(obj)
reason := c.GetReason(obj)
message := c.GetMessage(obj)
checkObj := obj
retObj, err := c.doInternal(setReturned, obj, f)
if setReturned {
checkObj = retObj
}
// This is to prevent non stop flapping of states and update
if status == c.GetStatus(checkObj) &&
reason == c.GetReason(checkObj) {
if message != c.GetMessage(checkObj) {
replaced := temfileRegexp.ReplaceAllString(c.GetMessage(checkObj), "file_path_redacted")
c.Message(checkObj, replaced)
}
if message == c.GetMessage(checkObj) {
c.LastUpdated(checkObj, ts)
}
}
changed := status != c.GetStatus(checkObj) ||
ts != c.GetLastUpdated(checkObj) ||
reason != c.GetReason(checkObj) ||
message != c.GetMessage(checkObj)
return retObj, changed, err
}
func (c Cond) doInternal(setReturned bool, obj runtime.Object, f func() (runtime.Object, error)) (runtime.Object, error) {
if !c.IsFalse(obj) {
c.Unknown(obj)
}
setObject := obj
newObj, err := f()
if newObj != nil && !reflect.ValueOf(newObj).IsNil() {
obj = newObj
if setReturned {
setObject = obj
}
}
if err != nil {
if _, ok := err.(*controller.ForgetError); ok {
if c.GetMessage(setObject) == "" {
c.ReasonAndMessageFromError(setObject, err)
}
return obj, err
}
c.False(setObject)
c.ReasonAndMessageFromError(setObject, err)
return obj, err
}
c.True(setObject)
c.Reason(setObject, "")
c.Message(setObject, "")
return obj, 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 := findOrNotCreateCond(obj, condName)
if cond == nil {
return ""
}
return getFieldValue(*cond, "Status").String()
}
func setTS(obj interface{}, condName, ts string) {
cond := findOrCreateCond(obj, condName)
getFieldValue(cond, "LastUpdateTime").SetString(ts)
}
func getTS(obj interface{}, condName string) string {
cond := findOrNotCreateCond(obj, condName)
if cond == nil {
return ""
}
return getFieldValue(*cond, "LastUpdateTime").String()
}
func setStatus(obj interface{}, condName, status string) {
cond := findOrCreateCond(obj, condName)
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)
}
}
func findOrNotCreateCond(obj interface{}, condName string) *reflect.Value {
condSlice := getValue(obj, "Status", "Conditions")
return findCond(condSlice, condName)
}
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 *findCond(condSlice, condName)
}
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 {
if obj == nil {
return 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:]...)
}
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
}
type GenericCondition struct {
// Type of cluster condition.
Type string `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status v1.ConditionStatus `json:"status"`
// The last time this condition was updated.
LastUpdateTime string `json:"lastUpdateTime,omitempty"`
// Last time the condition transitioned from one status to another.
LastTransitionTime string `json:"lastTransitionTime,omitempty"`
// The reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// Human-readable message indicating details about last transition
Message string `json:"message,omitempty"`
}