• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SAP / sap-btp-service-operator / 17354254741

31 Aug 2025 07:45AM UTC coverage: 78.37% (-1.6%) from 80.011%
17354254741

Pull #538

github

kerenlahav
delete irrelevant comments
Pull Request #538: transient error - prototype

52 of 142 new or added lines in 5 files covered. (36.62%)

17 existing lines in 3 files now uncovered.

2750 of 3509 relevant lines covered (78.37%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.95
/internal/utils/condition_utils.go
1
package utils
2

3
import (
4
        "context"
5
        "fmt"
6

7
        "github.com/SAP/sap-btp-service-operator/api/common"
8
        smClientTypes "github.com/SAP/sap-btp-service-operator/client/sm/types"
9
        "k8s.io/apimachinery/pkg/api/meta"
10
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11
        ctrl "sigs.k8s.io/controller-runtime"
12
        "sigs.k8s.io/controller-runtime/pkg/client"
13
)
14

15
func InitConditions(ctx context.Context, k8sClient client.Client, obj common.SAPBTPResource) error {
1✔
16
        obj.SetReady(metav1.ConditionFalse)
1✔
17
        SetInProgressConditions(ctx, smClientTypes.CREATE, "Pending", obj, false)
1✔
18
        return UpdateStatus(ctx, k8sClient, obj)
1✔
19
}
1✔
20

21
func GetConditionReason(opType smClientTypes.OperationCategory, state smClientTypes.OperationState) string {
1✔
22
        switch state {
1✔
23
        case smClientTypes.SUCCEEDED:
1✔
24
                if opType == smClientTypes.CREATE {
2✔
25
                        return common.Created
1✔
26
                } else if opType == smClientTypes.UPDATE {
3✔
27
                        return common.Updated
1✔
28
                } else if opType == smClientTypes.DELETE {
3✔
29
                        return common.Deleted
1✔
30
                }
1✔
31
                return common.Finished
1✔
32
        case smClientTypes.INPROGRESS, smClientTypes.PENDING:
1✔
33
                if opType == smClientTypes.CREATE {
2✔
34
                        return common.CreateInProgress
1✔
35
                } else if opType == smClientTypes.UPDATE {
3✔
36
                        return common.UpdateInProgress
1✔
37
                } else if opType == smClientTypes.DELETE {
3✔
38
                        return common.DeleteInProgress
1✔
39
                }
1✔
40
                return common.InProgress
1✔
41
        case smClientTypes.FAILED:
1✔
42
                if opType == smClientTypes.CREATE {
2✔
43
                        return common.CreateFailed
1✔
44
                } else if opType == smClientTypes.UPDATE {
3✔
45
                        return common.UpdateFailed
1✔
46
                } else if opType == smClientTypes.DELETE {
3✔
47
                        return common.DeleteFailed
1✔
48
                }
1✔
49
                return common.Failed
1✔
50
        }
51

52
        return common.Unknown
1✔
53
}
54

55
func SetInProgressConditions(ctx context.Context, operationType smClientTypes.OperationCategory, message string, object common.SAPBTPResource, isAsyncOperation bool) {
1✔
56
        log := GetLogger(ctx)
1✔
57
        if len(message) == 0 {
2✔
58
                if operationType == smClientTypes.CREATE {
2✔
59
                        message = fmt.Sprintf("%s is being created", object.GetControllerName())
1✔
60
                } else if operationType == smClientTypes.UPDATE {
1✔
UNCOV
61
                        message = fmt.Sprintf("%s is being updated", object.GetControllerName())
×
UNCOV
62
                } else if operationType == smClientTypes.DELETE {
×
63
                        message = fmt.Sprintf("%s is being deleted", object.GetControllerName())
×
NEW
64
                } else {
×
NEW
65
                        message = "Operation in progress"
×
UNCOV
66
                }
×
67
        }
68

69
        conditions := object.GetConditions()
1✔
70
        if len(conditions) > 0 {
1✔
UNCOV
71
                meta.RemoveStatusCondition(&conditions, common.ConditionFailed)
×
UNCOV
72
        }
×
73
        observedGen := object.GetGeneration()
1✔
74
        if isAsyncOperation {
1✔
75
                observedGen = getLastObservedGen(object)
×
76
        }
×
77
        lastOpCondition := metav1.Condition{
1✔
78
                Type:               common.ConditionSucceeded,
1✔
79
                Status:             metav1.ConditionFalse,
1✔
80
                Reason:             GetConditionReason(operationType, smClientTypes.INPROGRESS),
1✔
81
                Message:            message,
1✔
82
                ObservedGeneration: observedGen,
1✔
83
        }
1✔
84
        meta.SetStatusCondition(&conditions, lastOpCondition)
1✔
85
        meta.SetStatusCondition(&conditions, getReadyCondition(object))
1✔
86

1✔
87
        object.SetConditions(conditions)
1✔
88
        log.Info(fmt.Sprintf("setting inProgress conditions: reason: %s, message:%s, generation: %d", lastOpCondition.Reason, message, object.GetGeneration()))
1✔
89
}
90

91
func SetSuccessConditions(operationType smClientTypes.OperationCategory, object common.SAPBTPResource, isAsyncOperation bool) {
1✔
92
        var message string
1✔
93
        if operationType == smClientTypes.CREATE {
2✔
94
                message = fmt.Sprintf("%s provisioned successfully", object.GetControllerName())
1✔
95
        } else if operationType == smClientTypes.UPDATE {
1✔
96
                message = fmt.Sprintf("%s updated successfully", object.GetControllerName())
×
97
        } else if operationType == smClientTypes.DELETE {
×
98
                message = fmt.Sprintf("%s deleted successfully", object.GetControllerName())
×
99
        }
×
100

101
        conditions := object.GetConditions()
1✔
102
        if len(conditions) > 0 {
1✔
103
                meta.RemoveStatusCondition(&conditions, common.ConditionFailed)
×
104
        }
×
105
        observedGen := object.GetGeneration()
1✔
106
        if isAsyncOperation {
1✔
107
                observedGen = getLastObservedGen(object)
×
108
        }
×
109
        lastOpCondition := metav1.Condition{
1✔
110
                Type:               common.ConditionSucceeded,
1✔
111
                Status:             metav1.ConditionTrue,
1✔
112
                Reason:             GetConditionReason(operationType, smClientTypes.SUCCEEDED),
1✔
113
                Message:            message,
1✔
114
                ObservedGeneration: observedGen,
1✔
115
        }
1✔
116
        readyCondition := metav1.Condition{
1✔
117
                Type:               common.ConditionReady,
1✔
118
                Status:             metav1.ConditionTrue,
1✔
119
                Reason:             common.Provisioned,
1✔
120
                Message:            message,
1✔
121
                ObservedGeneration: observedGen,
1✔
122
        }
1✔
123
        meta.SetStatusCondition(&conditions, lastOpCondition)
1✔
124
        meta.SetStatusCondition(&conditions, readyCondition)
1✔
125

1✔
126
        object.SetConditions(conditions)
1✔
127
        object.SetReady(metav1.ConditionTrue)
1✔
128
}
129

130
func SetCredRotationInProgressConditions(reason, message string, object common.SAPBTPResource) {
1✔
131
        if len(message) == 0 {
1✔
132
                message = reason
×
133
        }
×
134
        conditions := object.GetConditions()
1✔
135
        credRotCondition := metav1.Condition{
1✔
136
                Type:               common.ConditionCredRotationInProgress,
1✔
137
                Status:             metav1.ConditionTrue,
1✔
138
                Reason:             reason,
1✔
139
                Message:            message,
1✔
140
                ObservedGeneration: object.GetGeneration(),
1✔
141
        }
1✔
142
        meta.SetStatusCondition(&conditions, credRotCondition)
1✔
143
        object.SetConditions(conditions)
1✔
144
}
145

146
func SetFailureConditions(operationType smClientTypes.OperationCategory, errorMessage string, object common.SAPBTPResource, isAsyncOperation bool) {
1✔
147
        var message string
1✔
148
        if operationType == smClientTypes.CREATE {
2✔
149
                message = fmt.Sprintf("%s create failed: %s", object.GetControllerName(), errorMessage)
1✔
150
        } else if operationType == smClientTypes.UPDATE {
1✔
151
                message = fmt.Sprintf("%s update failed: %s", object.GetControllerName(), errorMessage)
×
152
        } else if operationType == smClientTypes.DELETE {
×
153
                message = fmt.Sprintf("%s deletion failed: %s", object.GetControllerName(), errorMessage)
×
154
        }
×
155

156
        var reason string
1✔
157
        if operationType != common.Unknown {
2✔
158
                reason = GetConditionReason(operationType, smClientTypes.FAILED)
1✔
159
        } else {
1✔
160
                reason = object.GetConditions()[0].Reason
×
161
        }
×
162

163
        observedGen := object.GetGeneration()
1✔
164
        if isAsyncOperation {
1✔
165
                observedGen = getLastObservedGen(object)
×
166
        }
×
167
        conditions := object.GetConditions()
1✔
168
        lastOpCondition := metav1.Condition{
1✔
169
                Type:               common.ConditionSucceeded,
1✔
170
                Status:             metav1.ConditionFalse,
1✔
171
                Reason:             reason,
1✔
172
                Message:            message,
1✔
173
                ObservedGeneration: observedGen,
1✔
174
        }
1✔
175
        failedCondition := metav1.Condition{
1✔
176
                Type:               common.ConditionFailed,
1✔
177
                Status:             metav1.ConditionTrue,
1✔
178
                Reason:             reason,
1✔
179
                Message:            message,
1✔
180
                ObservedGeneration: observedGen,
1✔
181
        }
1✔
182

1✔
183
        meta.SetStatusCondition(&conditions, lastOpCondition)
1✔
184
        meta.SetStatusCondition(&conditions, failedCondition)
1✔
185
        meta.SetStatusCondition(&conditions, getReadyCondition(object))
1✔
186

1✔
187
        object.SetConditions(conditions)
1✔
188
}
189

NEW
190
func HandleOperationFailure(ctx context.Context, k8sClient client.Client, object common.SAPBTPResource, operationType smClientTypes.OperationCategory, err error) (ctrl.Result, error) {
×
UNCOV
191
        log := GetLogger(ctx)
×
UNCOV
192
        log.Info(fmt.Sprintf("operation %s of %s encountered a transient error %s, retrying operation :)", operationType, object.GetControllerName(), err.Error()))
×
NEW
193

×
NEW
194
        conditions := object.GetConditions()
×
NEW
195
        meta.RemoveStatusCondition(&conditions, common.ConditionFailed)
×
NEW
196
        lastOpCondition := metav1.Condition{
×
NEW
197
                Type:               common.ConditionSucceeded,
×
NEW
198
                Status:             metav1.ConditionFalse,
×
NEW
199
                Reason:             GetConditionReason(operationType, smClientTypes.FAILED),
×
NEW
200
                Message:            err.Error(),
×
NEW
201
                ObservedGeneration: object.GetGeneration(),
×
NEW
202
        }
×
NEW
203
        meta.SetStatusCondition(&conditions, lastOpCondition)
×
NEW
204
        meta.SetStatusCondition(&conditions, getReadyCondition(object))
×
NEW
205
        object.SetConditions(conditions)
×
NEW
206

×
UNCOV
207
        if updateErr := UpdateStatus(ctx, k8sClient, object); updateErr != nil {
×
UNCOV
208
                return ctrl.Result{}, updateErr
×
UNCOV
209
        }
×
210

NEW
211
        log.Info(fmt.Sprintf("Successfully updated last operation condition as failed with message {%s}, requeuing", lastOpCondition.Message))
×
NEW
212

×
UNCOV
213
        return ctrl.Result{}, err
×
214
}
215

216
// blocked condition marks to the user that action from his side is required, this is considered as in progress operation
217
func SetBlockedCondition(ctx context.Context, message string, object common.SAPBTPResource) {
1✔
218
        SetInProgressConditions(ctx, common.Unknown, message, object, false)
1✔
219
        lastOpCondition := meta.FindStatusCondition(object.GetConditions(), common.ConditionSucceeded)
1✔
220
        lastOpCondition.Reason = common.Blocked
1✔
221
}
1✔
222

223
func ShouldRetryOperation(object common.SAPBTPResource) bool {
1✔
224
        conditions := object.GetConditions()
1✔
225
        return meta.IsStatusConditionPresentAndEqual(conditions, common.ConditionSucceeded, metav1.ConditionFalse) &&
1✔
226
                !meta.IsStatusConditionPresentAndEqual(conditions, common.ConditionFailed, metav1.ConditionTrue) //failed condition is used in async operations - we don't want to retry async operations
1✔
227
}
1✔
228

NEW
229
func SetSharedCondition(object common.SAPBTPResource, status metav1.ConditionStatus, reason, msg string) {
×
NEW
230
        conditions := object.GetConditions()
×
NEW
231
        // align all conditions to latest generation
×
NEW
232
        for _, cond := range object.GetConditions() {
×
NEW
233
                if cond.Type != common.ConditionShared {
×
NEW
234
                        cond.ObservedGeneration = object.GetGeneration()
×
NEW
235
                        meta.SetStatusCondition(&conditions, cond)
×
NEW
236
                }
×
237
        }
238

NEW
239
        shareCondition := metav1.Condition{
×
NEW
240
                Type:    common.ConditionShared,
×
NEW
241
                Status:  status,
×
NEW
242
                Reason:  reason,
×
NEW
243
                Message: msg,
×
NEW
244
                // shared condition does not contain observed generation
×
NEW
245
        }
×
NEW
246

×
NEW
247
        // remove shared condition and add it as new (in case it has observed generation)
×
NEW
248
        meta.RemoveStatusCondition(&conditions, common.ConditionShared)
×
NEW
249
        meta.SetStatusCondition(&conditions, shareCondition)
×
NEW
250

×
NEW
251
        object.SetConditions(conditions)
×
252
}
253

254
func IsFailed(resource common.SAPBTPResource) bool {
1✔
255
        if len(resource.GetConditions()) == 0 {
2✔
256
                return false
1✔
257
        }
1✔
258
        return meta.IsStatusConditionPresentAndEqual(resource.GetConditions(), common.ConditionFailed, metav1.ConditionTrue) ||
1✔
259
                (resource.GetConditions()[0].Status == metav1.ConditionFalse &&
1✔
260
                        resource.GetConditions()[0].Type == common.ConditionSucceeded &&
1✔
261
                        resource.GetConditions()[0].Reason == common.Blocked)
1✔
262
}
263

264
func getReadyCondition(object common.SAPBTPResource) metav1.Condition {
1✔
265
        status := metav1.ConditionFalse
1✔
266
        reason := common.NotProvisioned
1✔
267
        if object.GetReady() == metav1.ConditionTrue {
1✔
268
                status = metav1.ConditionTrue
×
269
                reason = common.Provisioned
×
270
        }
×
271

272
        return metav1.Condition{Type: common.ConditionReady, Status: status, Reason: reason}
1✔
273
}
274

275
func getLastObservedGen(object common.SAPBTPResource) int64 {
1✔
276
        conditions := object.GetConditions()
1✔
277
        cond := meta.FindStatusCondition(conditions, common.ConditionSucceeded)
1✔
278
        if cond != nil {
2✔
279
                return cond.ObservedGeneration
1✔
280
        }
1✔
281
        return 0
1✔
282
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc