condition_set.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. Copyright 2018 The Knative Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package v1alpha1
  14. import (
  15. "reflect"
  16. "sort"
  17. "time"
  18. "fmt"
  19. "github.com/knative/pkg/apis"
  20. corev1 "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. )
  23. // Conditions is the interface for a Resource that implements the getter and
  24. // setter for accessing a Condition collection.
  25. // +k8s:deepcopy-gen=true
  26. type ConditionsAccessor interface {
  27. GetConditions() Conditions
  28. SetConditions(Conditions)
  29. }
  30. // ConditionSet is an abstract collection of the possible ConditionType values
  31. // that a particular resource might expose. It also holds the "happy condition"
  32. // for that resource, which we define to be one of Ready or Succeeded depending
  33. // on whether it is a Living or Batch process respectively.
  34. // +k8s:deepcopy-gen=false
  35. type ConditionSet struct {
  36. happy ConditionType
  37. dependents []ConditionType
  38. }
  39. // ConditionManager allows a resource to operate on its Conditions using higher
  40. // order operations.
  41. type ConditionManager interface {
  42. // IsHappy looks at the happy condition and returns true if that condition is
  43. // set to true.
  44. IsHappy() bool
  45. // GetCondition finds and returns the Condition that matches the ConditionType
  46. // previously set on Conditions.
  47. GetCondition(t ConditionType) *Condition
  48. // SetCondition sets or updates the Condition on Conditions for Condition.Type.
  49. // If there is an update, Conditions are stored back sorted.
  50. SetCondition(new Condition)
  51. // MarkTrue sets the status of t to true, and then marks the happy condition to
  52. // true if all dependents are true.
  53. MarkTrue(t ConditionType)
  54. // MarkUnknown sets the status of t to Unknown and also sets the happy condition
  55. // to Unknown if no other dependent condition is in an error state.
  56. MarkUnknown(t ConditionType, reason, messageFormat string, messageA ...interface{})
  57. // MarkFalse sets the status of t and the happy condition to False.
  58. MarkFalse(t ConditionType, reason, messageFormat string, messageA ...interface{})
  59. // InitializeConditions updates all Conditions in the ConditionSet to Unknown
  60. // if not set.
  61. InitializeConditions()
  62. // InitializeCondition updates a Condition to Unknown if not set.
  63. InitializeCondition(t ConditionType)
  64. }
  65. // NewLivingConditionSet returns a ConditionSet to hold the conditions for the
  66. // living resource. ConditionReady is used as the happy condition.
  67. // The set of condition types provided are those of the terminal subconditions.
  68. func NewLivingConditionSet(d ...ConditionType) ConditionSet {
  69. return newConditionSet(ConditionReady, d...)
  70. }
  71. // NewBatchConditionSet returns a ConditionSet to hold the conditions for the
  72. // batch resource. ConditionSucceeded is used as the happy condition.
  73. // The set of condition types provided are those of the terminal subconditions.
  74. func NewBatchConditionSet(d ...ConditionType) ConditionSet {
  75. return newConditionSet(ConditionSucceeded, d...)
  76. }
  77. // newConditionSet returns a ConditionSet to hold the conditions that are
  78. // important for the caller. The first ConditionType is the overarching status
  79. // for that will be used to signal the resources' status is Ready or Succeeded.
  80. func newConditionSet(happy ConditionType, dependents ...ConditionType) ConditionSet {
  81. var deps []ConditionType
  82. for _, d := range dependents {
  83. // Skip duplicates
  84. if d == happy || contains(deps, d) {
  85. continue
  86. }
  87. deps = append(deps, d)
  88. }
  89. return ConditionSet{
  90. happy: happy,
  91. dependents: deps,
  92. }
  93. }
  94. func contains(ct []ConditionType, t ConditionType) bool {
  95. for _, c := range ct {
  96. if c == t {
  97. return true
  98. }
  99. }
  100. return false
  101. }
  102. // Check that conditionsImpl implements ConditionManager.
  103. var _ ConditionManager = (*conditionsImpl)(nil)
  104. // conditionsImpl implements the helper methods for evaluating Conditions.
  105. // +k8s:deepcopy-gen=false
  106. type conditionsImpl struct {
  107. ConditionSet
  108. accessor ConditionsAccessor
  109. }
  110. // Manage creates a ConditionManager from a accessor object using the original
  111. // ConditionSet as a reference. Status must be or point to a struct.
  112. func (r ConditionSet) Manage(status interface{}) ConditionManager {
  113. // First try to see if status implements ConditionsAccessor
  114. ca, ok := status.(ConditionsAccessor)
  115. if ok {
  116. return conditionsImpl{
  117. accessor: ca,
  118. ConditionSet: r,
  119. }
  120. }
  121. // Next see if we can use reflection to gain access to Conditions
  122. ca = NewReflectedConditionsAccessor(status)
  123. if ca != nil {
  124. return conditionsImpl{
  125. accessor: ca,
  126. ConditionSet: r,
  127. }
  128. }
  129. // We tried. This object is not understood by the the condition manager.
  130. //panic(fmt.Sprintf("Error converting %T into a ConditionsAccessor", status))
  131. // TODO: not sure which way. using panic above means passing nil status panics the system.
  132. return conditionsImpl{
  133. ConditionSet: r,
  134. }
  135. }
  136. // IsHappy looks at the happy condition and returns true if that condition is
  137. // set to true.
  138. func (r conditionsImpl) IsHappy() bool {
  139. if c := r.GetCondition(r.happy); c == nil || !c.IsTrue() {
  140. return false
  141. }
  142. return true
  143. }
  144. // GetCondition finds and returns the Condition that matches the ConditionType
  145. // previously set on Conditions.
  146. func (r conditionsImpl) GetCondition(t ConditionType) *Condition {
  147. if r.accessor == nil {
  148. return nil
  149. }
  150. for _, c := range r.accessor.GetConditions() {
  151. if c.Type == t {
  152. return &c
  153. }
  154. }
  155. return nil
  156. }
  157. // SetCondition sets or updates the Condition on Conditions for Condition.Type.
  158. // If there is an update, Conditions are stored back sorted.
  159. func (r conditionsImpl) SetCondition(new Condition) {
  160. if r.accessor == nil {
  161. return
  162. }
  163. t := new.Type
  164. var conditions Conditions
  165. for _, c := range r.accessor.GetConditions() {
  166. if c.Type != t {
  167. conditions = append(conditions, c)
  168. } else {
  169. // If we'd only update the LastTransitionTime, then return.
  170. new.LastTransitionTime = c.LastTransitionTime
  171. if reflect.DeepEqual(&new, &c) {
  172. return
  173. }
  174. }
  175. }
  176. new.LastTransitionTime = apis.VolatileTime{Inner: metav1.NewTime(time.Now())}
  177. conditions = append(conditions, new)
  178. // Sorted for convenience of the consumer, i.e. kubectl.
  179. sort.Slice(conditions, func(i, j int) bool { return conditions[i].Type < conditions[j].Type })
  180. r.accessor.SetConditions(conditions)
  181. }
  182. func (r conditionsImpl) isTerminal(t ConditionType) bool {
  183. for _, cond := range append(r.dependents, r.happy) {
  184. if cond == t {
  185. return true
  186. }
  187. }
  188. return false
  189. }
  190. func (r conditionsImpl) severity(t ConditionType) ConditionSeverity {
  191. if r.isTerminal(t) {
  192. return ConditionSeverityError
  193. }
  194. return ConditionSeverityInfo
  195. }
  196. // MarkTrue sets the status of t to true, and then marks the happy condition to
  197. // true if all other dependents are also true.
  198. func (r conditionsImpl) MarkTrue(t ConditionType) {
  199. // set the specified condition
  200. r.SetCondition(Condition{
  201. Type: t,
  202. Status: corev1.ConditionTrue,
  203. Severity: r.severity(t),
  204. })
  205. // check the dependents.
  206. for _, cond := range r.dependents {
  207. c := r.GetCondition(cond)
  208. // Failed or Unknown conditions trump true conditions
  209. if !c.IsTrue() {
  210. return
  211. }
  212. }
  213. // set the happy condition
  214. r.SetCondition(Condition{
  215. Type: r.happy,
  216. Status: corev1.ConditionTrue,
  217. Severity: r.severity(r.happy),
  218. })
  219. }
  220. // MarkUnknown sets the status of t to Unknown and also sets the happy condition
  221. // to Unknown if no other dependent condition is in an error state.
  222. func (r conditionsImpl) MarkUnknown(t ConditionType, reason, messageFormat string, messageA ...interface{}) {
  223. // set the specified condition
  224. r.SetCondition(Condition{
  225. Type: t,
  226. Status: corev1.ConditionUnknown,
  227. Reason: reason,
  228. Message: fmt.Sprintf(messageFormat, messageA...),
  229. Severity: r.severity(t),
  230. })
  231. // check the dependents.
  232. isDependent := false
  233. for _, cond := range r.dependents {
  234. c := r.GetCondition(cond)
  235. // Failed conditions trump Unknown conditions
  236. if c.IsFalse() {
  237. // Double check that the happy condition is also false.
  238. happy := r.GetCondition(r.happy)
  239. if !happy.IsFalse() {
  240. r.MarkFalse(r.happy, reason, messageFormat, messageA)
  241. }
  242. return
  243. }
  244. if cond == t {
  245. isDependent = true
  246. }
  247. }
  248. if isDependent {
  249. // set the happy condition, if it is one of our dependent subconditions.
  250. r.SetCondition(Condition{
  251. Type: r.happy,
  252. Status: corev1.ConditionUnknown,
  253. Reason: reason,
  254. Message: fmt.Sprintf(messageFormat, messageA...),
  255. Severity: r.severity(r.happy),
  256. })
  257. }
  258. }
  259. // MarkFalse sets the status of t and the happy condition to False.
  260. func (r conditionsImpl) MarkFalse(t ConditionType, reason, messageFormat string, messageA ...interface{}) {
  261. types := []ConditionType{t}
  262. for _, cond := range r.dependents {
  263. if cond == t {
  264. types = append(types, r.happy)
  265. }
  266. }
  267. for _, t := range types {
  268. r.SetCondition(Condition{
  269. Type: t,
  270. Status: corev1.ConditionFalse,
  271. Reason: reason,
  272. Message: fmt.Sprintf(messageFormat, messageA...),
  273. Severity: r.severity(t),
  274. })
  275. }
  276. }
  277. // InitializeConditions updates all Conditions in the ConditionSet to Unknown
  278. // if not set.
  279. func (r conditionsImpl) InitializeConditions() {
  280. for _, t := range append(r.dependents, r.happy) {
  281. r.InitializeCondition(t)
  282. }
  283. }
  284. // InitializeCondition updates a Condition to Unknown if not set.
  285. func (r conditionsImpl) InitializeCondition(t ConditionType) {
  286. if c := r.GetCondition(t); c == nil {
  287. r.SetCondition(Condition{
  288. Type: t,
  289. Status: corev1.ConditionUnknown,
  290. Severity: r.severity(t),
  291. })
  292. }
  293. }
  294. // NewReflectedConditionsAccessor uses reflection to return a ConditionsAccessor
  295. // to access the field called "Conditions".
  296. func NewReflectedConditionsAccessor(status interface{}) ConditionsAccessor {
  297. statusValue := reflect.Indirect(reflect.ValueOf(status))
  298. // If status is not a struct, don't even try to use it.
  299. if statusValue.Kind() != reflect.Struct {
  300. return nil
  301. }
  302. conditionsField := statusValue.FieldByName("Conditions")
  303. if conditionsField.IsValid() && conditionsField.CanInterface() && conditionsField.CanSet() {
  304. if _, ok := conditionsField.Interface().(Conditions); ok {
  305. return &reflectedConditionsAccessor{
  306. conditions: conditionsField,
  307. }
  308. }
  309. }
  310. return nil
  311. }
  312. // reflectedConditionsAccessor is an internal wrapper object to act as the
  313. // ConditionsAccessor for status objects that do not implement ConditionsAccessor
  314. // directly, but do expose the field using the "Conditions" field name.
  315. type reflectedConditionsAccessor struct {
  316. conditions reflect.Value
  317. }
  318. // GetConditions uses reflection to return Conditions from the held status object.
  319. func (r *reflectedConditionsAccessor) GetConditions() Conditions {
  320. if r != nil && r.conditions.IsValid() && r.conditions.CanInterface() {
  321. if conditions, ok := r.conditions.Interface().(Conditions); ok {
  322. return conditions
  323. }
  324. }
  325. return Conditions(nil)
  326. }
  327. // SetConditions uses reflection to set Conditions on the held status object.
  328. func (r *reflectedConditionsAccessor) SetConditions(conditions Conditions) {
  329. if r != nil && r.conditions.IsValid() && r.conditions.CanSet() {
  330. r.conditions.Set(reflect.ValueOf(conditions))
  331. }
  332. }