sigs.k8s.io/cluster-api@v1.7.1/util/conditions/setter.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package conditions 18 19 import ( 20 "fmt" 21 "sort" 22 "time" 23 24 corev1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 27 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 28 ) 29 30 // Setter interface defines methods that a Cluster API object should implement in order to 31 // use the conditions package for setting conditions. 32 type Setter interface { 33 Getter 34 SetConditions(clusterv1.Conditions) 35 } 36 37 // Set sets the given condition. 38 // 39 // NOTE: If a condition already exists, the LastTransitionTime is updated only if a change is detected 40 // in any of the following fields: Status, Reason, Severity and Message. 41 func Set(to Setter, condition *clusterv1.Condition) { 42 if to == nil || condition == nil { 43 return 44 } 45 46 // Check if the new conditions already exists, and change it only if there is a status 47 // transition (otherwise we should preserve the current last transition time)- 48 conditions := to.GetConditions() 49 exists := false 50 for i := range conditions { 51 existingCondition := conditions[i] 52 if existingCondition.Type == condition.Type { 53 exists = true 54 if !hasSameState(&existingCondition, condition) { 55 condition.LastTransitionTime = metav1.NewTime(time.Now().UTC().Truncate(time.Second)) 56 conditions[i] = *condition 57 break 58 } 59 condition.LastTransitionTime = existingCondition.LastTransitionTime 60 break 61 } 62 } 63 64 // If the condition does not exist, add it, setting the transition time only if not already set 65 if !exists { 66 if condition.LastTransitionTime.IsZero() { 67 condition.LastTransitionTime = metav1.NewTime(time.Now().UTC().Truncate(time.Second)) 68 } 69 conditions = append(conditions, *condition) 70 } 71 72 // Sorts conditions for convenience of the consumer, i.e. kubectl. 73 sort.Slice(conditions, func(i, j int) bool { 74 return lexicographicLess(&conditions[i], &conditions[j]) 75 }) 76 77 to.SetConditions(conditions) 78 } 79 80 // TrueCondition returns a condition with Status=True and the given type. 81 func TrueCondition(t clusterv1.ConditionType) *clusterv1.Condition { 82 return &clusterv1.Condition{ 83 Type: t, 84 Status: corev1.ConditionTrue, 85 } 86 } 87 88 // FalseCondition returns a condition with Status=False and the given type. 89 func FalseCondition(t clusterv1.ConditionType, reason string, severity clusterv1.ConditionSeverity, messageFormat string, messageArgs ...interface{}) *clusterv1.Condition { 90 return &clusterv1.Condition{ 91 Type: t, 92 Status: corev1.ConditionFalse, 93 Reason: reason, 94 Severity: severity, 95 Message: fmt.Sprintf(messageFormat, messageArgs...), 96 } 97 } 98 99 // UnknownCondition returns a condition with Status=Unknown and the given type. 100 func UnknownCondition(t clusterv1.ConditionType, reason string, messageFormat string, messageArgs ...interface{}) *clusterv1.Condition { 101 return &clusterv1.Condition{ 102 Type: t, 103 Status: corev1.ConditionUnknown, 104 Reason: reason, 105 Message: fmt.Sprintf(messageFormat, messageArgs...), 106 } 107 } 108 109 // MarkTrue sets Status=True for the condition with the given type. 110 func MarkTrue(to Setter, t clusterv1.ConditionType) { 111 Set(to, TrueCondition(t)) 112 } 113 114 // MarkUnknown sets Status=Unknown for the condition with the given type. 115 func MarkUnknown(to Setter, t clusterv1.ConditionType, reason, messageFormat string, messageArgs ...interface{}) { 116 Set(to, UnknownCondition(t, reason, messageFormat, messageArgs...)) 117 } 118 119 // MarkFalse sets Status=False for the condition with the given type. 120 func MarkFalse(to Setter, t clusterv1.ConditionType, reason string, severity clusterv1.ConditionSeverity, messageFormat string, messageArgs ...interface{}) { 121 Set(to, FalseCondition(t, reason, severity, messageFormat, messageArgs...)) 122 } 123 124 // SetSummary sets a Ready condition with the summary of all the conditions existing 125 // on an object. If the object does not have other conditions, no summary condition is generated. 126 func SetSummary(to Setter, options ...MergeOption) { 127 Set(to, summary(to, options...)) 128 } 129 130 // SetMirror creates a new condition by mirroring the Ready condition from a dependent object; 131 // if the Ready condition does not exists in the source object, no target conditions is generated. 132 func SetMirror(to Setter, targetCondition clusterv1.ConditionType, from Getter, options ...MirrorOptions) { 133 Set(to, mirror(from, targetCondition, options...)) 134 } 135 136 // SetAggregate creates a new condition with the aggregation of all the Ready condition 137 // from a list of dependent objects; if the Ready condition does not exists in one of the source object, 138 // the object is excluded from the aggregation; if none of the source object have ready condition, 139 // no target conditions is generated. 140 func SetAggregate(to Setter, targetCondition clusterv1.ConditionType, from []Getter, options ...MergeOption) { 141 Set(to, aggregate(from, targetCondition, options...)) 142 } 143 144 // Delete deletes the condition with the given type. 145 func Delete(to Setter, t clusterv1.ConditionType) { 146 if to == nil { 147 return 148 } 149 150 conditions := to.GetConditions() 151 newConditions := make(clusterv1.Conditions, 0, len(conditions)) 152 for _, condition := range conditions { 153 if condition.Type != t { 154 newConditions = append(newConditions, condition) 155 } 156 } 157 to.SetConditions(newConditions) 158 } 159 160 // lexicographicLess returns true if a condition is less than another with regards to the 161 // to order of conditions designed for convenience of the consumer, i.e. kubectl. 162 // According to this order the Ready condition always goes first, followed by all the other 163 // conditions sorted by Type. 164 func lexicographicLess(i, j *clusterv1.Condition) bool { 165 return (i.Type == clusterv1.ReadyCondition || i.Type < j.Type) && j.Type != clusterv1.ReadyCondition 166 } 167 168 // hasSameState returns true if a condition has the same state of another; state is defined 169 // by the union of following fields: Type, Status, Reason, Severity and Message (it excludes LastTransitionTime). 170 func hasSameState(i, j *clusterv1.Condition) bool { 171 return i.Type == j.Type && 172 i.Status == j.Status && 173 i.Reason == j.Reason && 174 i.Severity == j.Severity && 175 i.Message == j.Message 176 }