Skip to content

Commit 09a5c31

Browse files
committed
chore: Print additional error message, if CheCluster controller failed to reconcile
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
1 parent 678d590 commit 09a5c31

File tree

24 files changed

+357
-288
lines changed

24 files changed

+357
-288
lines changed

controllers/che/checluster_controller.go

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ package che
1414

1515
import (
1616
"context"
17+
"time"
1718

19+
"github.com/eclipse-che/che-operator/pkg/common/constants"
20+
"github.com/eclipse-che/che-operator/pkg/common/reconciler"
1821
"k8s.io/utils/pointer"
1922
"sigs.k8s.io/controller-runtime/pkg/controller"
2023

@@ -47,7 +50,6 @@ import (
4750
chev2 "github.com/eclipse-che/che-operator/api/v2"
4851
"github.com/go-logr/logr"
4952
routev1 "github.com/openshift/api/route/v1"
50-
"github.com/sirupsen/logrus"
5153
appsv1 "k8s.io/api/apps/v1"
5254
corev1 "k8s.io/api/core/v1"
5355
networking "k8s.io/api/networking/v1"
@@ -75,8 +77,8 @@ type CheClusterReconciler struct {
7577
nonCachedClient client.Client
7678
// A discovery client to check for the existence of certain APIs registered
7779
// in the API Server
78-
discoveryClient discovery.DiscoveryInterface
79-
reconcileManager *deploy.ReconcileManager
80+
discoveryClient discovery.DiscoveryInterface
81+
reconcilerManager *reconciler.ReconcilerManager
8082
// the namespace to which to limit the reconciliation. If empty, all namespaces are considered
8183
namespace string
8284
}
@@ -89,49 +91,49 @@ func NewReconciler(
8991
scheme *k8sruntime.Scheme,
9092
namespace string) *CheClusterReconciler {
9193

92-
reconcileManager := deploy.NewReconcileManager()
94+
reconcilerManager := reconciler.NewReconcilerManager()
9395

9496
// order does matter
9597
if !test.IsTestMode() {
96-
reconcileManager.RegisterReconciler(migration.NewMigrator())
97-
reconcileManager.RegisterReconciler(migration.NewCheClusterDefaultsCleaner())
98-
reconcileManager.RegisterReconciler(NewCheClusterValidator())
98+
reconcilerManager.AddReconciler(migration.NewMigrator())
99+
reconcilerManager.AddReconciler(migration.NewCheClusterDefaultsCleaner())
100+
reconcilerManager.AddReconciler(NewCheClusterValidator())
99101
}
100102

101-
reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler())
102-
reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler())
103-
reconcileManager.RegisterReconciler(devworkspaceconfig.NewDevWorkspaceConfigReconciler())
104-
reconcileManager.RegisterReconciler(rbac.NewGatewayPermissionsReconciler())
103+
reconcilerManager.AddReconciler(tls.NewCertificatesReconciler())
104+
reconcilerManager.AddReconciler(tls.NewTlsSecretReconciler())
105+
reconcilerManager.AddReconciler(devworkspaceconfig.NewDevWorkspaceConfigReconciler())
106+
reconcilerManager.AddReconciler(rbac.NewGatewayPermissionsReconciler())
105107

106108
// we have to expose che endpoint independently of syncing other server
107109
// resources since che host is used for dashboard deployment and che config map
108-
reconcileManager.RegisterReconciler(server.NewCheHostReconciler())
109-
reconcileManager.RegisterReconciler(postgres.NewPostgresReconciler())
110+
reconcilerManager.AddReconciler(server.NewCheHostReconciler())
111+
reconcilerManager.AddReconciler(postgres.NewPostgresReconciler())
110112
if infrastructure.IsOpenShift() {
111-
reconcileManager.RegisterReconciler(identityprovider.NewIdentityProviderReconciler())
113+
reconcilerManager.AddReconciler(identityprovider.NewIdentityProviderReconciler())
112114
}
113-
reconcileManager.RegisterReconciler(devfileregistry.NewDevfileRegistryReconciler())
114-
reconcileManager.RegisterReconciler(pluginregistry.NewPluginRegistryReconciler())
115-
reconcileManager.RegisterReconciler(editorsdefinitions.NewEditorsDefinitionsReconciler())
116-
reconcileManager.RegisterReconciler(dashboard.NewDashboardReconciler())
117-
reconcileManager.RegisterReconciler(gateway.NewGatewayReconciler())
118-
reconcileManager.RegisterReconciler(server.NewCheServerReconciler())
119-
reconcileManager.RegisterReconciler(imagepuller.NewImagePuller())
115+
reconcilerManager.AddReconciler(devfileregistry.NewDevfileRegistryReconciler())
116+
reconcilerManager.AddReconciler(pluginregistry.NewPluginRegistryReconciler())
117+
reconcilerManager.AddReconciler(editorsdefinitions.NewEditorsDefinitionsReconciler())
118+
reconcilerManager.AddReconciler(dashboard.NewDashboardReconciler())
119+
reconcilerManager.AddReconciler(gateway.NewGatewayReconciler())
120+
reconcilerManager.AddReconciler(server.NewCheServerReconciler())
121+
reconcilerManager.AddReconciler(imagepuller.NewImagePuller())
120122

121123
if infrastructure.IsOpenShift() {
122-
reconcileManager.RegisterReconciler(containerbuild.NewContainerBuildReconciler())
123-
reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler())
124+
reconcilerManager.AddReconciler(containerbuild.NewContainerBuildReconciler())
125+
reconcilerManager.AddReconciler(consolelink.NewConsoleLinkReconciler())
124126
}
125127

126128
return &CheClusterReconciler{
127129
Scheme: scheme,
128130
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
129131

130-
client: k8sclient,
131-
nonCachedClient: noncachedClient,
132-
discoveryClient: discoveryClient,
133-
namespace: namespace,
134-
reconcileManager: reconcileManager,
132+
client: k8sclient,
133+
nonCachedClient: noncachedClient,
134+
discoveryClient: discoveryClient,
135+
namespace: namespace,
136+
reconcilerManager: reconcilerManager,
135137
}
136138
}
137139

@@ -236,21 +238,42 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
236238
deployContext.IsSelfSignedCertificate = isSelfSignedCertificate
237239

238240
if deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
239-
result, done, err := r.reconcileManager.ReconcileAll(deployContext)
241+
result, done, err := r.reconcilerManager.ReconcileAll(deployContext)
240242
if !done {
243+
if err != nil {
244+
errMsg := "Error on reconciling resources. The installation is not completed."
245+
r.Log.Error(err, errMsg)
246+
247+
if err := deploy.SetStatusDetails(deployContext, constants.InstallOrUpdateFailed, errMsg); err != nil {
248+
return ctrl.Result{}, err
249+
}
250+
}
251+
241252
return result, err
242253
} else {
254+
// Clean up status if so
243255
if err := deploy.SetStatusDetails(deployContext, "", ""); err != nil {
244256
return ctrl.Result{}, err
245257
}
246-
logrus.Info("Successfully reconciled.")
258+
259+
r.Log.Info("Successfully reconciled.")
247260
return ctrl.Result{}, nil
248261
}
249262
} else {
250263
deployContext.CheCluster.Status.ChePhase = chev2.ClusterPhasePendingDeletion
251-
_ = deploy.UpdateCheCRStatus(deployContext, "ChePhase", chev2.ClusterPhasePendingDeletion)
264+
if err = deploy.UpdateCheCRStatus(deployContext, "ChePhase", chev2.ClusterPhasePendingDeletion); err != nil {
265+
return ctrl.Result{}, err
266+
}
252267

253-
done := r.reconcileManager.FinalizeAll(deployContext)
254-
return ctrl.Result{Requeue: !done}, nil
268+
done := r.reconcilerManager.FinalizeAll(deployContext)
269+
if done {
270+
// Removes remaining finalizers, which prevent CheCluster from deletion
271+
if err := deploy.CleanUpAllFinalizers(deployContext); err != nil {
272+
return ctrl.Result{}, err
273+
}
274+
return ctrl.Result{}, nil
275+
} else {
276+
return ctrl.Result{RequeueAfter: time.Second}, nil
277+
}
255278
}
256279
}

controllers/che/checluster_validator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ import (
1717

1818
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
1919
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
20-
"github.com/eclipse-che/che-operator/pkg/deploy"
20+
"github.com/eclipse-che/che-operator/pkg/common/reconciler"
2121
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2222
)
2323

2424
// CheClusterValidator checks CheCluster CR configuration.
2525
// It detects:
2626
// - configurations which miss required field(s) to deploy Che
2727
type CheClusterValidator struct {
28-
deploy.Reconcilable
28+
reconciler.Reconcilable
2929
}
3030

3131
func NewCheClusterValidator() *CheClusterValidator {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// Copyright (c) 2019-2025 Red Hat, Inc.
3+
// This program and the accompanying materials are made
4+
// available under the terms of the Eclipse Public License 2.0
5+
// which is available at https://www.eclipse.org/legal/epl-2.0/
6+
//
7+
// SPDX-License-Identifier: EPL-2.0
8+
//
9+
// Contributors:
10+
// Red Hat, Inc. - initial API and implementation
11+
//
12+
13+
package reconciler
14+
15+
import (
16+
"fmt"
17+
"reflect"
18+
19+
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
20+
"github.com/pkg/errors"
21+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
22+
)
23+
24+
// Reconcilable defines the interface for components that can be reconciled and finalized.
25+
type Reconcilable interface {
26+
// Reconcile performs a reconciliation step to ensure the desired state matches the actual state.
27+
// Returns:
28+
// - result: reconcile.Result indicating when to requeue (if needed)
29+
// - done: true if reconciliation completed successfully, false if it needs to be retried
30+
// - err: any error encountered during reconciliation
31+
Reconcile(ctx *chetypes.DeployContext) (result reconcile.Result, done bool, err error)
32+
33+
// Finalize performs cleanup operations before the resource is deleted.
34+
// All created resources should be removed, including:
35+
// - finalizers
36+
// - cluster scoped resources
37+
// Returns true if finalization completed successfully, false otherwise.
38+
// TODO make Finalize return error as well
39+
Finalize(ctx *chetypes.DeployContext) (done bool)
40+
}
41+
42+
// ReconcilerManager manages a collection of Reconcilable objects and executes them in order.
43+
type ReconcilerManager struct {
44+
reconcilers []Reconcilable
45+
}
46+
47+
func NewReconcilerManager() *ReconcilerManager {
48+
return &ReconcilerManager{
49+
reconcilers: make([]Reconcilable, 0),
50+
}
51+
}
52+
53+
func (r *ReconcilerManager) AddReconciler(reconciler Reconcilable) {
54+
r.reconcilers = append(r.reconcilers, reconciler)
55+
}
56+
57+
// ReconcileAll reconciles all registered reconcilers in the order they were added.
58+
// The reconciliation process stops at the first reconciler that returns done=false,
59+
// ensuring dependencies between reconcilers are respected.
60+
func (r *ReconcilerManager) ReconcileAll(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
61+
for _, reconciler := range r.reconcilers {
62+
result, done, err := reconciler.Reconcile(ctx)
63+
64+
// Stop reconciliation chain if current reconciler is not done
65+
if !done {
66+
if err != nil {
67+
name := reflect.TypeOf(reconciler).String()
68+
return result, false, errors.Wrap(err, fmt.Sprintf("%s reconciliation failed", name))
69+
} else {
70+
return result, false, nil
71+
}
72+
}
73+
}
74+
75+
return reconcile.Result{}, true, nil
76+
}
77+
78+
// FinalizeAll invokes the Finalize method on all registered reconcilers.
79+
// Unlike ReconcileAll, this method continues executing all finalizers even if one fails,
80+
// ensuring all cleanup operations have a chance to run.
81+
// Returns true if all finalizers completed successfully, false if any failed.
82+
func (r *ReconcilerManager) FinalizeAll(ctx *chetypes.DeployContext) (doneAll bool) {
83+
doneAll = true
84+
85+
for _, reconciler := range r.reconcilers {
86+
done := reconciler.Finalize(ctx)
87+
doneAll = doneAll && done
88+
}
89+
90+
return doneAll
91+
}

0 commit comments

Comments
 (0)