Skip to content

Commit 53d1354

Browse files
committed
controllers: Ensure console plugin creation after CSV verification
Delay the creation of the console plugin until after verifying that the required CSVs are in the Succeeded phase. This ensures the necessary CRDs are present in the cluster, preventing UI errors like "Model does not exist." Signed-off-by: Nitin Goyal <nigoyal@redhat.com>
1 parent 7897655 commit 53d1354

File tree

4 files changed

+88
-47
lines changed

4 files changed

+88
-47
lines changed

controllers/clusterversion_controller.go

+29-16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controllers
1919
import (
2020
"context"
2121
"fmt"
22+
"time"
2223

2324
configv1 "github.com/openshift/api/config/v1"
2425
"k8s.io/apimachinery/pkg/api/errors"
@@ -37,8 +38,9 @@ import (
3738
// ClusterVersionReconciler reconciles a ClusterVersion object
3839
type ClusterVersionReconciler struct {
3940
client.Client
40-
Scheme *runtime.Scheme
41-
ConsolePort int
41+
Scheme *runtime.Scheme
42+
ConsolePort int
43+
OperatorNamespace string
4244
}
4345

4446
//+kubebuilder:rbac:groups=config.openshift.io,resources=clusterversions,verbs=get;list;watch;create;update;patch;delete
@@ -58,7 +60,18 @@ func (r *ClusterVersionReconciler) Reconcile(ctx context.Context, req ctrl.Reque
5860
if err := r.Client.Get(context.TODO(), req.NamespacedName, &instance); err != nil {
5961
return ctrl.Result{}, err
6062
}
61-
if err := r.ensureConsolePlugin(instance.Status.Desired.Version); err != nil {
63+
64+
if err := r.ensureOdfConsoleConfigMapAndService(); err != nil {
65+
logger.Error(err, "Could not ensure configmap and service for odf-console deployment")
66+
return ctrl.Result{}, err
67+
}
68+
69+
if err := util.EnsureCSVsSucceeded(ctx, r.Client, r.OperatorNamespace, CsvsToBeSuceeded...); err != nil {
70+
logger.Error(err, "Could not ensure CSVs in Succeeded state")
71+
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 2}, nil
72+
}
73+
74+
if err := r.ensureConsolePluginAndCLIDownload(instance.Status.Desired.Version); err != nil {
6275
logger.Error(err, "Could not ensure compatibility for ODF consolePlugin")
6376
return ctrl.Result{}, err
6477
}
@@ -69,12 +82,7 @@ func (r *ClusterVersionReconciler) Reconcile(ctx context.Context, req ctrl.Reque
6982
// SetupWithManager sets up the controller with the Manager.
7083
func (r *ClusterVersionReconciler) SetupWithManager(mgr ctrl.Manager) error {
7184
err := mgr.Add(manager.RunnableFunc(func(context.Context) error {
72-
clusterVersion, err := util.DetermineOpenShiftVersion(r.Client)
73-
if err != nil {
74-
return err
75-
}
76-
77-
return r.ensureConsolePlugin(clusterVersion)
85+
return r.ensureOdfConsoleConfigMapAndService()
7886
}))
7987
if err != nil {
8088
return err
@@ -85,15 +93,10 @@ func (r *ClusterVersionReconciler) SetupWithManager(mgr ctrl.Manager) error {
8593
Complete(r)
8694
}
8795

88-
func (r *ClusterVersionReconciler) ensureConsolePlugin(clusterVersion string) error {
96+
func (r *ClusterVersionReconciler) ensureOdfConsoleConfigMapAndService() error {
8997
logger := log.FromContext(context.TODO())
90-
// The base path to where the request are sent
91-
basePath := console.GetBasePath(clusterVersion)
9298
nginxConf := console.NginxConf
9399

94-
// Customer portal link (CLI Tool download)
95-
portalLink := console.CUSTOMER_PORTAL_LINK
96-
97100
// Get ODF console Deployment
98101
odfConsoleDeployment := console.GetDeployment(OperatorNamespace)
99102
err := r.Client.Get(context.TODO(), types.NamespacedName{
@@ -126,9 +129,19 @@ func (r *ClusterVersionReconciler) ensureConsolePlugin(clusterVersion string) er
126129
return err
127130
}
128131

132+
return nil
133+
}
134+
135+
func (r *ClusterVersionReconciler) ensureConsolePluginAndCLIDownload(clusterVersion string) error {
136+
logger := log.FromContext(context.TODO())
137+
// The base path to where the request are sent
138+
basePath := console.GetBasePath(clusterVersion)
139+
// Customer portal link (CLI Tool download)
140+
portalLink := console.CUSTOMER_PORTAL_LINK
141+
129142
// Create/Update ODF console ConsolePlugin
130143
odfConsolePlugin := console.GetConsolePluginCR(r.ConsolePort, OperatorNamespace)
131-
_, err = controllerutil.CreateOrUpdate(context.TODO(), r.Client, odfConsolePlugin, func() error {
144+
_, err := controllerutil.CreateOrUpdate(context.TODO(), r.Client, odfConsolePlugin, func() error {
132145
if odfConsolePlugin.Spec.Backend.Service != nil {
133146
if currentBasePath := odfConsolePlugin.Spec.Backend.Service.BasePath; currentBasePath != basePath {
134147
logger.Info(fmt.Sprintf("Set the BasePath for odf-console plugin as '%s'", basePath))

controllers/defaults.go

+8
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ const (
172172
OdfSubscriptionPackage = "odf-operator"
173173
)
174174

175+
var (
176+
CsvsToBeSuceeded = []string{
177+
OcsSubscriptionStartingCSV,
178+
RookSubscriptionStartingCSV,
179+
NoobaaSubscriptionStartingCSV,
180+
}
181+
)
182+
175183
func GetEnvOrDefault(env string) string {
176184
if val := os.Getenv(env); val != "" {
177185
return val

main.go

+5-9
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,10 @@ func main() {
158158
}
159159

160160
if err = (&controllers.ClusterVersionReconciler{
161-
Client: mgr.GetClient(),
162-
Scheme: mgr.GetScheme(),
163-
ConsolePort: odfConsolePort,
161+
Client: mgr.GetClient(),
162+
Scheme: mgr.GetScheme(),
163+
ConsolePort: odfConsolePort,
164+
OperatorNamespace: operatorNamespace,
164165
}).SetupWithManager(mgr); err != nil {
165166
setupLog.Error(err, "unable to create controller", "controller", "ClusterVersion")
166167
os.Exit(1)
@@ -176,17 +177,12 @@ func main() {
176177
// can't check for only odf-dependencies CSV as OLM doesn't guarantee
177178
// (based on observations) existence of all CRDs brought by OLM dependency
178179
// mechanism.
179-
csvsToBeSuceeded := []string{
180-
controllers.OcsSubscriptionStartingCSV,
181-
controllers.RookSubscriptionStartingCSV,
182-
controllers.NoobaaSubscriptionStartingCSV,
183-
}
184180
if err := mgr.AddReadyzCheck(
185181
"readyz",
186182
util.CheckCSVPhase(
187183
mgr.GetClient(),
188184
operatorNamespace,
189-
csvsToBeSuceeded...,
185+
controllers.CsvsToBeSuceeded...,
190186
),
191187
); err != nil {
192188
setupLog.Error(err, "unable to set up ready check")

pkg/util/readiness.go

+46-22
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package util
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"net/http"
2223
"strings"
@@ -26,15 +27,12 @@ import (
2627
"sigs.k8s.io/controller-runtime/pkg/healthz"
2728
)
2829

29-
func CheckCSVPhase(c client.Client, namespace string, csvNames ...string) healthz.Checker {
30+
func CheckCSVPhase(cli client.Client, namespace string, csvNames ...string) healthz.Checker {
3031

31-
csvMap := map[string]struct{}{}
32-
for _, name := range csvNames {
33-
csvMap[name] = struct{}{}
34-
}
3532
return func(r *http.Request) error {
36-
csvList := &opv1a1.ClusterServiceVersionList{}
37-
if err := c.List(r.Context(), csvList, client.InNamespace(namespace)); err != nil {
33+
34+
csvList, err := GetNamespaceCSVs(r.Context(), cli, namespace)
35+
if err != nil {
3836
return err
3937
}
4038

@@ -45,24 +43,50 @@ func CheckCSVPhase(c client.Client, namespace string, csvNames ...string) health
4543
return nil
4644
}
4745

48-
for idx := range csvList.Items {
49-
csv := &csvList.Items[idx]
50-
_, exists := csvMap[csv.Name]
51-
if exists {
52-
if csv.Status.Phase != opv1a1.CSVPhaseSucceeded {
53-
return fmt.Errorf("CSV %s is not in Succeeded phase", csv.Name)
54-
} else if csv.Status.Phase == opv1a1.CSVPhaseSucceeded {
55-
delete(csvMap, csv.Name)
56-
}
57-
}
46+
return ValidateCSVsSucceeded(csvList, csvNames...)
47+
}
48+
}
49+
50+
func EnsureCSVsSucceeded(ctx context.Context, cli client.Client, namespace string, csvsToBeSuceeded ...string) error {
51+
52+
csvList, err := GetNamespaceCSVs(ctx, cli, namespace)
53+
if err != nil {
54+
return err
55+
}
56+
57+
return ValidateCSVsSucceeded(csvList, csvsToBeSuceeded...)
58+
}
59+
60+
func ValidateCSVsSucceeded(csvList *opv1a1.ClusterServiceVersionList, csvsToBeSucceeded ...string) error {
61+
62+
for _, csvName := range csvsToBeSucceeded {
63+
csv, found := GetCSVByName(csvList, csvName)
64+
if !found {
65+
return fmt.Errorf("CSV %q not found in the list", csvName)
66+
}
67+
68+
if csv.Status.Phase != opv1a1.CSVPhaseSucceeded {
69+
return fmt.Errorf("CSV %q is not in the Succeeded phase; current phase: %s", csv.Name, csv.Status.Phase)
5870
}
59-
if len(csvMap) != 0 {
60-
for csvName := range csvMap {
61-
return fmt.Errorf("CSV %s is not found", csvName)
62-
}
71+
}
72+
return nil
73+
}
74+
75+
func GetCSVByName(csvList *opv1a1.ClusterServiceVersionList, name string) (*opv1a1.ClusterServiceVersion, bool) {
76+
77+
for _, csv := range csvList.Items {
78+
if csv.Name == name {
79+
return &csv, true
6380
}
64-
return nil
6581
}
82+
return nil, false
83+
}
84+
85+
func GetNamespaceCSVs(ctx context.Context, cli client.Client, namespace string) (*opv1a1.ClusterServiceVersionList, error) {
86+
87+
csvList := &opv1a1.ClusterServiceVersionList{}
88+
err := cli.List(ctx, csvList, client.InNamespace(namespace))
89+
return csvList, err
6690
}
6791

6892
func AreMultipleOdfOperatorCsvsPresent(csvs *opv1a1.ClusterServiceVersionList) bool {

0 commit comments

Comments
 (0)