1
2
3
4 package status
5
6 import (
7 "errors"
8 "fmt"
9 "time"
10
11 corev1 "k8s.io/api/core/v1"
12 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13 )
14
15 const (
16
17
18
19 ConditionStalled ConditionType = "Stalled"
20 ConditionReconciling ConditionType = "Reconciling"
21
22
23 InProgressStatus Status = "InProgress"
24 FailedStatus Status = "Failed"
25 CurrentStatus Status = "Current"
26 TerminatingStatus Status = "Terminating"
27 NotFoundStatus Status = "NotFound"
28 UnknownStatus Status = "Unknown"
29 )
30
31 var (
32 Statuses = []Status{InProgressStatus, FailedStatus, CurrentStatus, TerminatingStatus, UnknownStatus}
33 )
34
35
36 type ConditionType string
37
38
39 func (c ConditionType) String() string {
40 return string(c)
41 }
42
43
44 type Status string
45
46
47 func (s Status) String() string {
48 return string(s)
49 }
50
51
52
53 func FromStringOrDie(text string) Status {
54 s := Status(text)
55 for _, r := range Statuses {
56 if s == r {
57 return s
58 }
59 }
60 panic(fmt.Errorf("string has invalid status: %s", s))
61 }
62
63
64
65 type Result struct {
66
67 Status Status
68
69 Message string
70
71 Conditions []Condition
72 }
73
74
75
76
77 type Condition struct {
78
79 Type ConditionType `json:"type,omitempty"`
80
81 Status corev1.ConditionStatus `json:"status,omitempty"`
82
83 Reason string `json:"reason,omitempty"`
84
85 Message string `json:"message,omitempty"`
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 func Compute(u *unstructured.Unstructured) (*Result, error) {
103 res, err := checkGenericProperties(u)
104 if err != nil {
105 return nil, err
106 }
107
108
109
110
111 if res != nil {
112 return res, nil
113 }
114
115 fn := GetLegacyConditionsFn(u)
116 if fn != nil {
117 return fn(u)
118 }
119
120
121
122
123
124
125 res, err = checkReadyCondition(u)
126 if res != nil || err != nil {
127 return res, err
128 }
129
130
131
132
133
134 return &Result{
135 Status: CurrentStatus,
136 Message: "Resource is current",
137 Conditions: []Condition{},
138 }, err
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 func checkReadyCondition(u *unstructured.Unstructured) (*Result, error) {
155 objWithConditions, err := GetObjectWithConditions(u.Object)
156 if err != nil {
157 return nil, err
158 }
159
160 for _, cond := range objWithConditions.Status.Conditions {
161 if cond.Type != "Ready" {
162 continue
163 }
164 switch cond.Status {
165 case corev1.ConditionTrue:
166 return &Result{
167 Status: CurrentStatus,
168 Message: "Resource is Ready",
169 Conditions: []Condition{},
170 }, nil
171 case corev1.ConditionFalse:
172 return newInProgressStatus(cond.Reason, cond.Message), nil
173 case corev1.ConditionUnknown:
174
175
176
177 return newInProgressStatus(cond.Reason, cond.Message), nil
178 default:
179
180 }
181 }
182 return nil, nil
183 }
184
185
186
187 func Augment(u *unstructured.Unstructured) error {
188 res, err := Compute(u)
189 if err != nil {
190 return err
191 }
192
193 conditions, found, err := unstructured.NestedSlice(u.Object, "status", "conditions")
194 if err != nil {
195 return err
196 }
197
198 if !found {
199 conditions = make([]interface{}, 0)
200 }
201
202 currentTime := time.Now().UTC().Format(time.RFC3339)
203
204 for _, resCondition := range res.Conditions {
205 present := false
206 for _, c := range conditions {
207 condition, ok := c.(map[string]interface{})
208 if !ok {
209 return errors.New("condition does not have the expected structure")
210 }
211 conditionType, ok := condition["type"].(string)
212 if !ok {
213 return errors.New("condition type does not have the expected type")
214 }
215 if conditionType == string(resCondition.Type) {
216 conditionStatus, ok := condition["status"].(string)
217 if !ok {
218 return errors.New("condition status does not have the expected type")
219 }
220 if conditionStatus != string(resCondition.Status) {
221 condition["lastTransitionTime"] = currentTime
222 }
223 condition["status"] = string(resCondition.Status)
224 condition["lastUpdateTime"] = currentTime
225 condition["reason"] = resCondition.Reason
226 condition["message"] = resCondition.Message
227 present = true
228 }
229 }
230 if !present {
231 conditions = append(conditions, map[string]interface{}{
232 "lastTransitionTime": currentTime,
233 "lastUpdateTime": currentTime,
234 "message": resCondition.Message,
235 "reason": resCondition.Reason,
236 "status": string(resCondition.Status),
237 "type": string(resCondition.Type),
238 })
239 }
240 }
241 return unstructured.SetNestedSlice(u.Object, conditions, "status", "conditions")
242 }
243
View as plain text