Skip to content

Commit c9f469d

Browse files
committed
OCPBUGS-52556: Remove v1alpha1 ConsolePlugin version from CRDs status
1 parent 1960690 commit c9f469d

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed

manifests/03-rbac-role-cluster.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,23 @@ rules:
131131
- apiextensions.k8s.io
132132
resources:
133133
- customresourcedefinitions
134+
- customresourcedefinitions/status
134135
verbs:
135136
- get
136137
- list
137138
- watch
139+
- patch
140+
resourceNames:
141+
- consoleplugins.console.openshift.io
142+
- apiGroups:
143+
- migration.k8s.io
144+
resources:
145+
- storageversionmigrations
146+
verbs:
147+
- get
148+
- list
149+
- watch
150+
- delete
138151
---
139152
kind: ClusterRole
140153
apiVersion: rbac.authorization.k8s.io/v1
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
package storageversionmigration
2+
3+
import (
4+
"context"
5+
"errors"
6+
"time"
7+
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10+
"k8s.io/apimachinery/pkg/runtime/schema"
11+
"k8s.io/apimachinery/pkg/types"
12+
"k8s.io/apimachinery/pkg/util/json"
13+
"k8s.io/client-go/dynamic"
14+
"k8s.io/client-go/dynamic/dynamicinformer"
15+
"k8s.io/klog/v2"
16+
17+
"github.com/openshift/console-operator/pkg/console/status"
18+
"github.com/openshift/library-go/pkg/controller/factory"
19+
"github.com/openshift/library-go/pkg/operator/events"
20+
"github.com/openshift/library-go/pkg/operator/v1helpers"
21+
)
22+
23+
const (
24+
storageVersionMigrationName = "console-plugin-storage-version-migration"
25+
consolePluginCRDName = "consoleplugins.console.openshift.io"
26+
maxRetries = 5
27+
retryDelay = 2 * time.Second
28+
)
29+
30+
var (
31+
storageVersionMigrationGVR = schema.GroupVersionResource{
32+
Group: "migration.k8s.io",
33+
Version: "v1alpha1",
34+
Resource: "storageversionmigrations",
35+
}
36+
crdGVR = schema.GroupVersionResource{
37+
Group: "apiextensions.k8s.io",
38+
Version: "v1",
39+
Resource: "customresourcedefinitions",
40+
}
41+
)
42+
43+
type StorageVersionMigrationController struct {
44+
dynamicClient dynamic.Interface
45+
operatorClient v1helpers.OperatorClient
46+
}
47+
48+
func NewStorageVersionMigrationController(
49+
operatorClient v1helpers.OperatorClient,
50+
dynamicClient dynamic.Interface,
51+
dynamicInformers dynamicinformer.DynamicSharedInformerFactory,
52+
recorder events.Recorder,
53+
) factory.Controller {
54+
c := &StorageVersionMigrationController{
55+
dynamicClient: dynamicClient,
56+
operatorClient: operatorClient,
57+
}
58+
59+
return factory.New().
60+
WithInformers(
61+
dynamicInformers.ForResource(storageVersionMigrationGVR).Informer(),
62+
).
63+
WithSync(c.sync).
64+
ToController("StorageVersionMigrationController", recorder)
65+
}
66+
67+
func (c *StorageVersionMigrationController) sync(ctx context.Context, syncContext factory.SyncContext) error {
68+
statusHandler := status.NewStatusHandler(c.operatorClient)
69+
70+
// Check if the ConsolePlugin CRD still has v1alpha1 in storedVersions
71+
hasV1Alpha1, err := c.checkCRDStoredVersions(ctx)
72+
if err != nil {
73+
klog.Errorf("Failed to check CRD storedVersions: %v", err)
74+
statusHandler.AddCondition(status.HandleDegraded("StorageVersionMigration", "FailedCheckCRDStoredVersions", err))
75+
return statusHandler.FlushAndReturn(err)
76+
}
77+
78+
if !hasV1Alpha1 {
79+
klog.V(4).Infof("ConsolePlugin CRD does not have v1alpha1 in storedVersions, migration already complete")
80+
return statusHandler.FlushAndReturn(nil)
81+
}
82+
83+
// Get the StorageVersionMigration instance
84+
svm, err := c.dynamicClient.Resource(storageVersionMigrationGVR).Get(ctx, storageVersionMigrationName, metav1.GetOptions{})
85+
if err != nil {
86+
klog.Errorf("Failed to get StorageVersionMigration: %v", err)
87+
return statusHandler.FlushAndReturn(err)
88+
}
89+
90+
// Check if the migration has succeeded with retry logic
91+
succeeded, err := c.hasSucceededConditionWithRetry(svm)
92+
if err != nil {
93+
klog.Errorf("Failed to check conditions for StorageVersionMigration after %d retries: %v", maxRetries, err)
94+
return statusHandler.FlushAndReturn(err)
95+
}
96+
97+
if !succeeded {
98+
klog.V(4).Infof("StorageVersionMigration has not succeeded yet")
99+
// Delete the StorageVersionMigration if it has not succeeded yet
100+
if err := c.deleteStorageVersionMigration(ctx); err != nil {
101+
statusHandler.AddCondition(status.HandleDegraded("StorageVersionMigration", "FailedDeleteSVM", err))
102+
return statusHandler.FlushAndReturn(err)
103+
}
104+
return statusHandler.FlushAndReturn(nil)
105+
}
106+
107+
klog.Infof("StorageVersionMigration has succeeded, setting ConsolePlugin CRD storedVersions to v1")
108+
109+
// Set CRD storedVersions to v1
110+
if err := c.removeV1Alpha1FromCRD(ctx); err != nil {
111+
statusHandler.AddCondition(status.HandleDegraded("StorageVersionMigration", "FailedPatchCRD", err))
112+
return statusHandler.FlushAndReturn(err)
113+
}
114+
115+
return statusHandler.FlushAndReturn(nil)
116+
}
117+
118+
// checkCRDStoredVersions checks if the ConsolePlugin CRD has v1alpha1 in its storedVersions
119+
func (c *StorageVersionMigrationController) checkCRDStoredVersions(ctx context.Context) (bool, error) {
120+
// Get the ConsolePlugin CRD
121+
crd, err := c.dynamicClient.Resource(crdGVR).Get(ctx, consolePluginCRDName, metav1.GetOptions{})
122+
if err != nil {
123+
klog.Errorf("Failed to get ConsolePlugin CRD: %v", err)
124+
return false, err
125+
}
126+
127+
// Get storedVersions from the CRD status
128+
storedVersions, found, err := unstructured.NestedStringSlice(crd.Object, "status", "storedVersions")
129+
if err != nil {
130+
klog.Errorf("Failed to get storedVersions for ConsolePlugin CRD: %v", err)
131+
return false, err
132+
}
133+
if !found {
134+
klog.V(4).Infof("No storedVersions found for ConsolePlugin CRD")
135+
return false, nil
136+
}
137+
138+
// Check if v1alpha1 is present in storedVersions
139+
for _, version := range storedVersions {
140+
if version == "v1alpha1" {
141+
klog.V(4).Infof("Found v1alpha1 in ConsolePlugin CRD storedVersions: %v", storedVersions)
142+
return true, nil
143+
}
144+
}
145+
146+
klog.V(4).Infof("v1alpha1 not found in ConsolePlugin CRD storedVersions: %v", storedVersions)
147+
return false, nil
148+
}
149+
150+
// hasSucceededConditionWithRetry checks if the StorageVersionMigration has succeeded with retry logic
151+
func (c *StorageVersionMigrationController) hasSucceededConditionWithRetry(svm *unstructured.Unstructured) (bool, error) {
152+
var lastErr error
153+
for attempt := 1; attempt <= maxRetries; attempt++ {
154+
succeeded, err := c.hasSucceededCondition(svm)
155+
if err == nil {
156+
return succeeded, nil
157+
}
158+
159+
lastErr = err
160+
klog.Warningf("Attempt %d/%d failed to check StorageVersionMigration conditions: %v", attempt, maxRetries, err)
161+
162+
if attempt < maxRetries {
163+
klog.V(4).Infof("Retrying in %v...", retryDelay)
164+
time.Sleep(retryDelay)
165+
}
166+
}
167+
168+
return false, lastErr
169+
}
170+
171+
// hasSucceededCondition checks if the StorageVersionMigration has a 'Succeeded' condition with status 'True'
172+
func (c *StorageVersionMigrationController) hasSucceededCondition(svm *unstructured.Unstructured) (bool, error) {
173+
conditions, found, err := unstructured.NestedSlice(svm.Object, "status", "conditions")
174+
if err != nil {
175+
return false, err
176+
}
177+
if !found {
178+
return false, errors.New("conditions not found")
179+
}
180+
181+
for _, condition := range conditions {
182+
conditionMap, ok := condition.(map[string]interface{})
183+
if !ok {
184+
continue
185+
}
186+
187+
conditionType, typeFound := conditionMap["type"].(string)
188+
conditionStatus, statusFound := conditionMap["status"].(string)
189+
190+
if typeFound && statusFound && conditionType == "Succeeded" && conditionStatus == "True" {
191+
return true, nil
192+
}
193+
}
194+
195+
return false, nil
196+
}
197+
198+
// removeV1Alpha1FromCRD removes 'v1alpha1' from the ConsolePlugin CRD's storedVersions
199+
func (c *StorageVersionMigrationController) removeV1Alpha1FromCRD(ctx context.Context) error {
200+
// Get the ConsolePlugin CRD
201+
crd, err := c.dynamicClient.Resource(crdGVR).Get(ctx, consolePluginCRDName, metav1.GetOptions{})
202+
if err != nil {
203+
klog.Errorf("Failed to get ConsolePlugin CRD: %v", err)
204+
return err
205+
}
206+
207+
// Verify CRD exists and has status
208+
_, found, err := unstructured.NestedMap(crd.Object, "status")
209+
if err != nil {
210+
klog.Errorf("Failed to get status for ConsolePlugin CRD: %v", err)
211+
return err
212+
}
213+
if !found {
214+
klog.Info("No status found for ConsolePlugin CRD")
215+
return nil
216+
}
217+
218+
// Set storedVersions to only contain v1
219+
newStoredVersions := []string{"v1"}
220+
221+
// Create and apply patch
222+
return c.patchCRDStoredVersions(ctx, newStoredVersions)
223+
}
224+
225+
// patchCRDStoredVersions applies a patch to update the CRD's storedVersions
226+
func (c *StorageVersionMigrationController) patchCRDStoredVersions(ctx context.Context, newStoredVersions []string) error {
227+
patch := map[string]interface{}{
228+
"status": map[string]interface{}{
229+
"storedVersions": newStoredVersions,
230+
},
231+
}
232+
233+
patchBytes, err := json.Marshal(patch)
234+
if err != nil {
235+
klog.Errorf("Failed to marshal patch for ConsolePlugin CRD: %v", err)
236+
return err
237+
}
238+
239+
// Apply the patch to the status subresource
240+
_, err = c.dynamicClient.Resource(crdGVR).Patch(ctx, consolePluginCRDName, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status")
241+
if err != nil {
242+
klog.Errorf("Failed to patch ConsolePlugin CRD status: %v", err)
243+
return err
244+
}
245+
246+
klog.Infof("Successfully set ConsolePlugin CRD storedVersions to v1")
247+
return nil
248+
}
249+
250+
// deleteStorageVersionMigration deletes the StorageVersionMigration resource
251+
func (c *StorageVersionMigrationController) deleteStorageVersionMigration(ctx context.Context) error {
252+
klog.Infof("Deleting StorageVersionMigration")
253+
err := c.dynamicClient.Resource(storageVersionMigrationGVR).Delete(ctx, storageVersionMigrationName, metav1.DeleteOptions{})
254+
if err != nil {
255+
klog.Errorf("Failed to delete StorageVersionMigration: %v", err)
256+
return err
257+
}
258+
return nil
259+
}

pkg/console/starter/starter.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
pdb "github.com/openshift/console-operator/pkg/console/controllers/poddisruptionbudget"
3737
"github.com/openshift/console-operator/pkg/console/controllers/route"
3838
"github.com/openshift/console-operator/pkg/console/controllers/service"
39+
"github.com/openshift/console-operator/pkg/console/controllers/storageversionmigration"
3940
upgradenotification "github.com/openshift/console-operator/pkg/console/controllers/upgradenotification"
4041
"github.com/openshift/console-operator/pkg/console/controllers/util"
4142
"github.com/openshift/library-go/pkg/controller/controllercmd"
@@ -556,6 +557,14 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle
556557
recorder,
557558
)
558559

560+
// Create the StorageVersionMigration controller
561+
storageversionmigrationController := storageversionmigration.NewStorageVersionMigrationController(
562+
operatorClient,
563+
dynamicClient,
564+
dynamicInformers,
565+
recorder,
566+
)
567+
559568
configUpgradeableController := unsupportedconfigoverridescontroller.NewUnsupportedConfigOverridesController("UnsupportedConfigOverridesController", operatorClient, controllerContext.EventRecorder)
560569
logLevelController := loglevel.NewClusterOperatorLoggingController(operatorClient, controllerContext.EventRecorder)
561570
managementStateController := managementstatecontroller.NewOperatorManagementStateController(api.ClusterOperatorName, operatorClient, controllerContext.EventRecorder)
@@ -604,6 +613,7 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle
604613
cliOIDCClientStatusController,
605614
upgradeNotificationController,
606615
staleConditionsController,
616+
storageversionmigrationController,
607617
} {
608618
go controller.Run(ctx, 1)
609619
}

0 commit comments

Comments
 (0)