Skip to content

Commit 84b0bb6

Browse files
authored
chore: Print additional error message, if CheCluster controller faile… (#2057)
* chore: Print additional error message, if CheCluster controller failed to reconcile Signed-off-by: Anatolii Bazko <[email protected]>
1 parent dd0065d commit 84b0bb6

26 files changed

+362
-296
lines changed

.devfile.Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
FROM quay.io/devfile/universal-developer-image:ubi8-latest
22

3-
#install Go 1.19
3+
#install Go 1.24.7
44
RUN cd /tmp && wget https://go.dev/dl/go1.24.7.linux-amd64.tar.gz && \
55
mkdir $HOME/go1.24.7 && \
6-
tar -xvzf go1.24.7.linux-amd64.tar.gz -C $HOME/go1.19 --strip-components 1 && \
6+
tar -xvzf go1.24.7.linux-amd64.tar.gz -C $HOME/go1.24.7 --strip-components 1 && \
77
if ! grep -q "export PATH=\$HOME/go1.19/bin:\$PATH" $HOME/.bashrc; then echo "export PATH=\$HOME/go1.24.7/bin:\$PATH" >> $HOME/.bashrc; fi
88

99
# install chectl

controllers/che/checluster_controller.go

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import (
1616
"context"
1717
"time"
1818

19+
"github.com/eclipse-che/che-operator/pkg/common/constants"
1920
k8sclient "github.com/eclipse-che/che-operator/pkg/common/k8s-client"
21+
"github.com/eclipse-che/che-operator/pkg/common/reconciler"
2022
"k8s.io/utils/pointer"
2123
"sigs.k8s.io/controller-runtime/pkg/controller"
2224

@@ -49,7 +51,6 @@ import (
4951
chev2 "github.com/eclipse-che/che-operator/api/v2"
5052
"github.com/go-logr/logr"
5153
routev1 "github.com/openshift/api/route/v1"
52-
"github.com/sirupsen/logrus"
5354
appsv1 "k8s.io/api/apps/v1"
5455
corev1 "k8s.io/api/core/v1"
5556
networking "k8s.io/api/networking/v1"
@@ -77,8 +78,8 @@ type CheClusterReconciler struct {
7778
nonCachedClient client.Client
7879
// A discovery client to check for the existence of certain APIs registered
7980
// in the API Server
80-
discoveryClient discovery.DiscoveryInterface
81-
reconcileManager *deploy.ReconcileManager
81+
discoveryClient discovery.DiscoveryInterface
82+
reconcilerManager *reconciler.ReconcilerManager
8283
// the namespace to which to limit the reconciliation. If empty, all namespaces are considered
8384
namespace string
8485
}
@@ -91,49 +92,49 @@ func NewReconciler(
9192
scheme *k8sruntime.Scheme,
9293
namespace string) *CheClusterReconciler {
9394

94-
reconcileManager := deploy.NewReconcileManager()
95+
reconcilerManager := reconciler.NewReconcilerManager()
9596

9697
// order does matter
9798
if !test.IsTestMode() {
98-
reconcileManager.RegisterReconciler(migration.NewMigrator())
99-
reconcileManager.RegisterReconciler(migration.NewCheClusterDefaultsCleaner())
100-
reconcileManager.RegisterReconciler(NewCheClusterValidator())
99+
reconcilerManager.AddReconciler(migration.NewMigrator())
100+
reconcilerManager.AddReconciler(migration.NewCheClusterDefaultsCleaner())
101+
reconcilerManager.AddReconciler(NewCheClusterValidator())
101102
}
102103

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

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

123124
if infrastructure.IsOpenShift() {
124-
reconcileManager.RegisterReconciler(containerbuild.NewContainerCapabilitiesReconciler())
125-
reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler())
125+
reconcilerManager.AddReconciler(containerbuild.NewContainerCapabilitiesReconciler())
126+
reconcilerManager.AddReconciler(consolelink.NewConsoleLinkReconciler())
126127
}
127128

128129
return &CheClusterReconciler{
129130
Scheme: scheme,
130131
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
131132

132-
client: k8sclient,
133-
nonCachedClient: noncachedClient,
134-
discoveryClient: discoveryClient,
135-
namespace: namespace,
136-
reconcileManager: reconcileManager,
133+
client: k8sclient,
134+
nonCachedClient: noncachedClient,
135+
discoveryClient: discoveryClient,
136+
namespace: namespace,
137+
reconcilerManager: reconcilerManager,
137138
}
138139
}
139140

@@ -240,24 +241,42 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
240241
deployContext.IsSelfSignedCertificate = isSelfSignedCertificate
241242

242243
if deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
243-
result, done, err := r.reconcileManager.ReconcileAll(deployContext)
244-
if !done {
245-
return result, err
246-
} else {
244+
result, done, err := r.reconcilerManager.ReconcileAll(deployContext)
245+
if done {
246+
// Clean up status if so
247247
if err := deploy.SetStatusDetails(deployContext, "", ""); err != nil {
248248
return ctrl.Result{}, err
249249
}
250-
logrus.Info("Successfully reconciled.")
250+
251+
r.Log.Info("Successfully reconciled.")
251252
return ctrl.Result{}, nil
253+
} else {
254+
if err != nil {
255+
errMsg := "Failed to reconcile CheCluster resources. The installation is not completed. Check operator logs for details."
256+
r.Log.Error(err, errMsg)
257+
258+
if err := deploy.SetStatusDetails(deployContext, constants.InstallOrUpdateFailed, errMsg); err != nil {
259+
return ctrl.Result{}, err
260+
}
261+
}
262+
263+
return result, err
252264
}
253265
} else {
254266
deployContext.CheCluster.Status.ChePhase = chev2.ClusterPhasePendingDeletion
255-
_ = deploy.UpdateCheCRStatus(deployContext, "ChePhase", chev2.ClusterPhasePendingDeletion)
256-
257-
if done := r.reconcileManager.FinalizeAll(deployContext); !done {
258-
return ctrl.Result{RequeueAfter: 1 * time.Second}, nil
267+
if err = deploy.UpdateCheCRStatus(deployContext, "ChePhase", chev2.ClusterPhasePendingDeletion); err != nil {
268+
return ctrl.Result{}, err
269+
}
259270

271+
done := r.reconcilerManager.FinalizeAll(deployContext)
272+
if done {
273+
// Removes remaining finalizers, which prevent CheCluster from deletion
274+
if err := deploy.CleanUpAllFinalizers(deployContext); err != nil {
275+
return ctrl.Result{}, err
276+
}
277+
return ctrl.Result{}, nil
278+
} else {
279+
return ctrl.Result{RequeueAfter: time.Second}, nil
260280
}
261-
return ctrl.Result{}, nil
262281
}
263282
}

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 {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ require (
5757
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
5858
github.com/onsi/gomega v1.36.1 // indirect; used for manifest generation
5959
github.com/operator-framework/operator-registry v1.17.5 // indirect
60-
github.com/pkg/errors v0.9.1 // indirect
60+
github.com/pkg/errors v0.9.1
6161
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
6262
github.com/prometheus/client_golang v1.22.0 // indirect
6363
github.com/prometheus/client_model v0.6.1 // indirect
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
"strings"
19+
20+
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
21+
"github.com/pkg/errors"
22+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
23+
)
24+
25+
// Reconcilable defines the interface for components that can be reconciled and finalized.
26+
type Reconcilable interface {
27+
// Reconcile performs a reconciliation step to ensure the desired state matches the actual state.
28+
// Returns:
29+
// - result: reconcile.Result indicating when to requeue (if needed)
30+
// - done: true if reconciliation completed successfully, false if it needs to be retried
31+
// - err: any error encountered during reconciliation
32+
Reconcile(ctx *chetypes.DeployContext) (result reconcile.Result, done bool, err error)
33+
34+
// Finalize performs cleanup operations before the resource is deleted.
35+
// All created resources should be removed, including:
36+
// - finalizers
37+
// - cluster scoped resources
38+
// Returns true if finalization completed successfully, false otherwise.
39+
// TODO make Finalize return error as well
40+
Finalize(ctx *chetypes.DeployContext) (done bool)
41+
}
42+
43+
// ReconcilerManager manages a collection of Reconcilable objects and executes them in order.
44+
type ReconcilerManager struct {
45+
reconcilers []Reconcilable
46+
}
47+
48+
func NewReconcilerManager() *ReconcilerManager {
49+
return &ReconcilerManager{
50+
reconcilers: make([]Reconcilable, 0),
51+
}
52+
}
53+
54+
func (r *ReconcilerManager) AddReconciler(reconciler Reconcilable) {
55+
r.reconcilers = append(r.reconcilers, reconciler)
56+
}
57+
58+
// ReconcileAll reconciles all registered reconcilers in the order they were added.
59+
// The reconciliation process stops at the first reconciler that returns done=false,
60+
// ensuring dependencies between reconcilers are respected.
61+
func (r *ReconcilerManager) ReconcileAll(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
62+
for _, reconciler := range r.reconcilers {
63+
result, done, err := reconciler.Reconcile(ctx)
64+
65+
// Stop reconciliation chain if current reconciler is not done
66+
if !done {
67+
if err != nil {
68+
name := strings.Trim(reflect.TypeOf(reconciler).String(), "*")
69+
return result, false, errors.Wrap(err, fmt.Sprintf("%s reconciliation failed", name))
70+
} else {
71+
return result, false, nil
72+
}
73+
}
74+
}
75+
76+
return reconcile.Result{}, true, nil
77+
}
78+
79+
// FinalizeAll invokes the Finalize method on all registered reconcilers.
80+
// Unlike ReconcileAll, this method continues executing all finalizers even if one fails,
81+
// ensuring all cleanup operations have a chance to run.
82+
// Returns true if all finalizers completed successfully, false if any failed.
83+
func (r *ReconcilerManager) FinalizeAll(ctx *chetypes.DeployContext) (doneAll bool) {
84+
doneAll = true
85+
86+
for _, reconciler := range r.reconcilers {
87+
done := reconciler.Finalize(ctx)
88+
doneAll = doneAll && done
89+
}
90+
91+
return doneAll
92+
}

0 commit comments

Comments
 (0)