@@ -48,6 +48,38 @@ var ErrOLMNotInstalled = errors.New("no existing installation found")
48
48
49
49
var Scheme = scheme .Scheme
50
50
51
+ // custom error struct to capture deployment errors
52
+ // while verifying CSV installs.
53
+ type resourceError struct {
54
+ name string
55
+ issue string
56
+ }
57
+ type podError struct {
58
+ resourceError
59
+ }
60
+ type deploymentError struct {
61
+ resourceError
62
+ podErrs podErrors
63
+ }
64
+ type deploymentErrors []deploymentError
65
+ type podErrors []podError
66
+
67
+ func (e deploymentErrors ) Error () string {
68
+ var sb strings.Builder
69
+ for _ , i := range e {
70
+ sb .WriteString (fmt .Sprintf ("deployment %s has error: %s\n %s" , i .name , i .issue , i .podErrs .Error ()))
71
+ }
72
+ return sb .String ()
73
+ }
74
+
75
+ func (e podErrors ) Error () string {
76
+ var sb strings.Builder
77
+ for _ , i := range e {
78
+ sb .WriteString (fmt .Sprintf ("\t pod %s has error: %s\n " , i .name , i .issue ))
79
+ }
80
+ return sb .String ()
81
+ }
82
+
51
83
func init () {
52
84
if err := olmapiv1alpha1 .AddToScheme (Scheme ); err != nil {
53
85
log .Fatalf ("Failed to add OLM operator API v1alpha1 types to scheme: %v" , err )
@@ -226,17 +258,18 @@ func (c Client) DoCSVWait(ctx context.Context, key types.NamespacedName) error {
226
258
227
259
err := wait .PollImmediateUntil (time .Second , csvPhaseSucceeded , ctx .Done ())
228
260
if err != nil && errors .Is (err , context .DeadlineExceeded ) {
229
- if depCheckErr := c .printDeploymentErrors (ctx , key , csv ); depCheckErr != nil {
230
- return fmt .Errorf ("error printing operator resource errors: %v %v" , err , depCheckErr )
261
+ depCheckErr := c .checkDeploymentErrors (ctx , key , csv )
262
+ if depCheckErr != nil {
263
+ return depCheckErr
231
264
}
232
265
}
233
266
return err
234
267
}
235
268
236
- // TODO(btenneti) Refactor function to collect errors into customized error and return.
237
- // printDeploymentErrors function loops through deployment specs of a given CSV, and prints reason
269
+ // checkDeploymentErrors function loops through deployment specs of a given CSV, and prints reason
238
270
// in case of failures, based on deployment condition.
239
- func (c Client ) printDeploymentErrors (ctx context.Context , key types.NamespacedName , csv olmapiv1alpha1.ClusterServiceVersion ) error {
271
+ func (c Client ) checkDeploymentErrors (ctx context.Context , key types.NamespacedName , csv olmapiv1alpha1.ClusterServiceVersion ) error {
272
+ depErrs := deploymentErrors {}
240
273
if key .Namespace == "" {
241
274
return fmt .Errorf ("no namespace provided to get deployment failures" )
242
275
}
@@ -248,25 +281,40 @@ func (c Client) printDeploymentErrors(ctx context.Context, key types.NamespacedN
248
281
}
249
282
depSelectors := ds .Spec .Selector
250
283
if err := c .KubeClient .Get (ctx , depKey , dep ); err != nil {
251
- log .Printf ("error getting operator deployment %q: %v" , ds .Name , err )
284
+ depErrs = append (depErrs , deploymentError {
285
+ resourceError : resourceError {
286
+ name : ds .Name ,
287
+ issue : err .Error (),
288
+ },
289
+ })
252
290
continue
253
291
}
254
292
for _ , s := range dep .Status .Conditions {
255
- if s .Type == appsv1 .DeploymentAvailable && s .Status == corev1 .ConditionFalse {
256
- log .Printf ("operator deployment %q not available: %s" , ds .Name , s .Reason )
257
- if err := c .printPodErrors (ctx , depSelectors , key ); err != nil {
258
- return err
293
+ if s .Type == appsv1 .DeploymentAvailable && s .Status != corev1 .ConditionTrue {
294
+ depErr := deploymentError {
295
+ resourceError : resourceError {
296
+ name : ds .Name ,
297
+ issue : s .Reason ,
298
+ },
299
+ }
300
+ podErr := c .checkPodErrors (ctx , depSelectors , key )
301
+ podErrs := podErrors {}
302
+ if errors .As (podErr , & podErrs ) {
303
+ depErr .podErrs = append (depErr .podErrs , podErrs ... )
304
+ } else {
305
+ return podErr
259
306
}
307
+ depErrs = append (depErrs , depErr )
260
308
}
261
309
}
262
310
}
263
- return nil
311
+ return depErrs
264
312
}
265
313
266
- // printPodErrors loops through pods, and prints pod errors if any.
267
- func (c Client ) printPodErrors (ctx context.Context , depSelectors * metav1.LabelSelector , key types.NamespacedName ) error {
314
+ // checkPodErrors loops through pods, and returns pod errors if any.
315
+ func (c Client ) checkPodErrors (ctx context.Context , depSelectors * metav1.LabelSelector , key types.NamespacedName ) error {
268
316
// loop through pods and return specific error message.
269
- podErrors := make ( map [ string ] string )
317
+ podErr := podErrors {}
270
318
podList := & corev1.PodList {}
271
319
podLabelSelectors , err := metav1 .LabelSelectorAsSelector (depSelectors )
272
320
if err != nil {
@@ -280,18 +328,21 @@ func (c Client) printPodErrors(ctx context.Context, depSelectors *metav1.LabelSe
280
328
return fmt .Errorf ("error getting Pods: %v" , err )
281
329
}
282
330
for _ , p := range podList .Items {
283
- if p .Status .Phase != corev1 .PodSucceeded {
284
- for _ , cs := range p .Status .ContainerStatuses {
285
- if ! cs .Ready {
286
- podErrors [p .Name ] = cs .State .Waiting .Message
331
+ for _ , cs := range p .Status .ContainerStatuses {
332
+ if ! cs .Ready {
333
+ if cs .State .Waiting != nil {
334
+ containerName := p .Name + ":" + cs .Name
335
+ podErr = append (podErr , podError {
336
+ resourceError {
337
+ name : containerName ,
338
+ issue : cs .State .Waiting .Message ,
339
+ },
340
+ })
287
341
}
288
342
}
289
343
}
290
344
}
291
- if len (podErrors ) > 0 {
292
- log .Printf ("pod errors: %v\n " , podErrors )
293
- }
294
- return nil
345
+ return podErr
295
346
}
296
347
297
348
// GetInstalledVersion returns the OLM version installed in the namespace informed.
0 commit comments