Skip to content

Commit dbd6113

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 present. 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 dbd6113

File tree

4 files changed

+99
-47
lines changed

4 files changed

+99
-47
lines changed

controllers/clusterversion_controller.go

+39-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,28 @@ 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+
csvList, err := util.GetNamespaceCSVs(ctx, r.Client, r.OperatorNamespace)
70+
if err != nil {
71+
return ctrl.Result{}, err
72+
}
73+
74+
// Check if it is upgrade from 4.17 to 4.18
75+
// The new CSVs won't exists while upgrading
76+
// They will exists only after new operator has created a new subscription
77+
if !util.AreMultipleOdfOperatorCsvsPresent(csvList) {
78+
if err := util.ValidateCSVsPresent(csvList, EssentialCSVs...); err != nil {
79+
logger.Error(err, "Could not ensure CSVs presence")
80+
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 2}, nil
81+
}
82+
}
83+
84+
if err := r.ensureConsolePluginAndCLIDownload(instance.Status.Desired.Version); err != nil {
6285
logger.Error(err, "Could not ensure compatibility for ODF consolePlugin")
6386
return ctrl.Result{}, err
6487
}
@@ -69,12 +92,7 @@ func (r *ClusterVersionReconciler) Reconcile(ctx context.Context, req ctrl.Reque
6992
// SetupWithManager sets up the controller with the Manager.
7093
func (r *ClusterVersionReconciler) SetupWithManager(mgr ctrl.Manager) error {
7194
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)
95+
return r.ensureOdfConsoleConfigMapAndService()
7896
}))
7997
if err != nil {
8098
return err
@@ -85,15 +103,10 @@ func (r *ClusterVersionReconciler) SetupWithManager(mgr ctrl.Manager) error {
85103
Complete(r)
86104
}
87105

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

94-
// Customer portal link (CLI Tool download)
95-
portalLink := console.CUSTOMER_PORTAL_LINK
96-
97110
// Get ODF console Deployment
98111
odfConsoleDeployment := console.GetDeployment(OperatorNamespace)
99112
err := r.Client.Get(context.TODO(), types.NamespacedName{
@@ -126,9 +139,19 @@ func (r *ClusterVersionReconciler) ensureConsolePlugin(clusterVersion string) er
126139
return err
127140
}
128141

142+
return nil
143+
}
144+
145+
func (r *ClusterVersionReconciler) ensureConsolePluginAndCLIDownload(clusterVersion string) error {
146+
logger := log.FromContext(context.TODO())
147+
// The base path to where the request are sent
148+
basePath := console.GetBasePath(clusterVersion)
149+
// Customer portal link (CLI Tool download)
150+
portalLink := console.CUSTOMER_PORTAL_LINK
151+
129152
// Create/Update ODF console ConsolePlugin
130153
odfConsolePlugin := console.GetConsolePluginCR(r.ConsolePort, OperatorNamespace)
131-
_, err = controllerutil.CreateOrUpdate(context.TODO(), r.Client, odfConsolePlugin, func() error {
154+
_, err := controllerutil.CreateOrUpdate(context.TODO(), r.Client, odfConsolePlugin, func() error {
132155
if odfConsolePlugin.Spec.Backend.Service != nil {
133156
if currentBasePath := odfConsolePlugin.Spec.Backend.Service.BasePath; currentBasePath != basePath {
134157
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+
EssentialCSVs = []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.EssentialCSVs...,
190186
),
191187
); err != nil {
192188
setupLog.Error(err, "unable to set up ready check")

pkg/util/readiness.go

+47-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,51 @@ 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 validateCSVsSucceeded(csvList *opv1a1.ClusterServiceVersionList, csvsToBeSucceeded ...string) error {
51+
52+
for _, csvName := range csvsToBeSucceeded {
53+
csv, found := getCSVByName(csvList, csvName)
54+
if !found {
55+
return fmt.Errorf("CSV %q not found in the list", csvName)
56+
}
57+
58+
if csv.Status.Phase != opv1a1.CSVPhaseSucceeded {
59+
return fmt.Errorf("CSV %q is not in the Succeeded phase; current phase: %s", csv.Name, csv.Status.Phase)
60+
}
61+
}
62+
return nil
63+
}
64+
65+
func ValidateCSVsPresent(csvList *opv1a1.ClusterServiceVersionList, csvsToBePresent ...string) error {
66+
67+
for _, csvName := range csvsToBePresent {
68+
_, found := getCSVByName(csvList, csvName)
69+
if !found {
70+
return fmt.Errorf("CSV %q not found in the list", csvName)
5871
}
59-
if len(csvMap) != 0 {
60-
for csvName := range csvMap {
61-
return fmt.Errorf("CSV %s is not found", csvName)
62-
}
72+
}
73+
return nil
74+
}
75+
76+
func getCSVByName(csvList *opv1a1.ClusterServiceVersionList, name string) (*opv1a1.ClusterServiceVersion, bool) {
77+
78+
for i := range csvList.Items {
79+
if csvList.Items[i].Name == name {
80+
return &csvList.Items[i], true
6381
}
64-
return nil
6582
}
83+
return nil, false
84+
}
85+
86+
func GetNamespaceCSVs(ctx context.Context, cli client.Client, namespace string) (*opv1a1.ClusterServiceVersionList, error) {
87+
88+
csvList := &opv1a1.ClusterServiceVersionList{}
89+
err := cli.List(ctx, csvList, client.InNamespace(namespace))
90+
return csvList, err
6691
}
6792

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

0 commit comments

Comments
 (0)