Skip to content

Commit ebdf019

Browse files
committed
Use Patch() to update status of CnsRegisterVolume CR insteaf of Update()
1 parent 69a18ea commit ebdf019

File tree

6 files changed

+351
-8
lines changed

6 files changed

+351
-8
lines changed

manifests/supervisorcluster/1.29/cns-csi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ rules:
5959
verbs: ["get", "list", "update", "create", "delete"]
6060
- apiGroups: ["cns.vmware.com"]
6161
resources: ["cnsregistervolumes", "cnsregistervolumes/status", "cnsunregistervolumes", "cnsunregistervolumes/status"]
62-
verbs: ["get", "list", "watch", "update", "delete"]
62+
verbs: ["get", "list", "watch", "update", "delete", "patch"]
6363
- apiGroups: ["cns.vmware.com"]
6464
resources: ["triggercsifullsyncs"]
6565
verbs: ["create", "get", "update", "watch", "list"]

manifests/supervisorcluster/1.30/cns-csi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ rules:
5959
verbs: ["get", "list", "update", "create", "delete"]
6060
- apiGroups: ["cns.vmware.com"]
6161
resources: ["cnsregistervolumes", "cnsregistervolumes/status", "cnsunregistervolumes", "cnsunregistervolumes/status"]
62-
verbs: ["get", "list", "watch", "update", "delete"]
62+
verbs: ["get", "list", "watch", "update", "delete", "patch"]
6363
- apiGroups: ["cns.vmware.com"]
6464
resources: ["triggercsifullsyncs"]
6565
verbs: ["create", "get", "update", "watch", "list"]

manifests/supervisorcluster/1.31/cns-csi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ rules:
5959
verbs: ["get", "list", "update", "create", "delete"]
6060
- apiGroups: ["cns.vmware.com"]
6161
resources: ["cnsregistervolumes", "cnsregistervolumes/status", "cnsunregistervolumes", "cnsunregistervolumes/status"]
62-
verbs: ["get", "list", "watch", "update", "delete"]
62+
verbs: ["get", "list", "watch", "update", "delete", "patch"]
6363
- apiGroups: ["cns.vmware.com"]
6464
resources: ["triggercsifullsyncs"]
6565
verbs: ["create", "get", "update", "watch", "list"]

manifests/supervisorcluster/1.32/cns-csi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ rules:
5959
verbs: ["get", "list", "update", "create", "delete"]
6060
- apiGroups: ["cns.vmware.com"]
6161
resources: ["cnsregistervolumes", "cnsregistervolumes/status", "cnsunregistervolumes", "cnsunregistervolumes/status"]
62-
verbs: ["get", "list", "watch", "update", "delete"]
62+
verbs: ["get", "list", "watch", "update", "delete", "patch"]
6363
- apiGroups: ["cns.vmware.com"]
6464
resources: ["triggercsifullsyncs"]
6565
verbs: ["create", "get", "update", "watch", "list"]

pkg/syncer/cnsoperator/controller/cnsregistervolume/cnsregistervolume_controller.go

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,10 +1173,12 @@ func validateAndFixPVVolumeMode(ctx context.Context, k8sclient clientset.Interfa
11731173
func setInstanceError(ctx context.Context, r *ReconcileCnsRegisterVolume,
11741174
instance *cnsregistervolumev1alpha1.CnsRegisterVolume, errMsg string) {
11751175
log := logger.GetLogger(ctx)
1176+
origInstance := instance.DeepCopy()
1177+
instance.Status.Registered = false
11761178
instance.Status.Error = errMsg
1177-
err := k8s.UpdateStatus(ctx, r.client, instance)
1179+
err := patchCnsRegisterVolumeStatus(ctx, r.client, origInstance, instance)
11781180
if err != nil {
1179-
log.Errorf("updateCnsRegisterVolume failed. err: %v", err)
1181+
log.Errorf("patchCnsRegisterVolumeStatus failed. err: %v", err)
11801182
return
11811183
}
11821184
recordEvent(ctx, r, instance, v1.EventTypeWarning, errMsg)
@@ -1205,11 +1207,12 @@ func setInstanceSuccess(ctx context.Context, r *ReconcileCnsRegisterVolume,
12051207
return err
12061208
}
12071209
// Now update the status subresource with the fresh object
1210+
origInstance := instance.DeepCopy()
12081211
instance.Status.Registered = true
12091212
instance.Status.Error = ""
1210-
err = k8s.UpdateStatus(ctx, r.client, instance)
1213+
err = patchCnsRegisterVolumeStatus(ctx, r.client, origInstance, instance)
12111214
if err != nil {
1212-
log.Errorf("updateCnsRegisterVolume status failed. err: %v", err)
1215+
log.Errorf("patchCnsRegisterVolumeStatus failed. err: %v", err)
12131216
return err
12141217
}
12151218
recordEvent(ctx, r, instance, v1.EventTypeNormal, msg)
@@ -1273,3 +1276,61 @@ func updateCnsRegisterVolume(ctx context.Context, client client.Client,
12731276
}
12741277
return err
12751278
}
1279+
1280+
// patchCnsRegisterVolumeStatus patches status field of CnsRegisterVolume instance in K8S.
1281+
// For status subresources with required fields, we need to ensure those fields are always
1282+
// included in the patch, even if they haven't changed. This is because Kubernetes validates
1283+
// the patch against the CRD schema which marks 'registered' as required.
1284+
func patchCnsRegisterVolumeStatus(ctx context.Context, cnsOperatorClient client.Client,
1285+
oldObj *cnsregistervolumev1alpha1.CnsRegisterVolume,
1286+
newObj *cnsregistervolumev1alpha1.CnsRegisterVolume) error {
1287+
log := logger.GetLogger(ctx)
1288+
1289+
// For status patches with required fields, we must always include 'registered' field
1290+
// to satisfy CRD validation. For the 'error' field (optional with omitempty):
1291+
// - Include it if it has a value
1292+
// - Include it as empty string only if we're explicitly clearing it (oldObj had error, newObj doesn't)
1293+
// - Omit it if both oldObj and newObj have no error (to avoid showing error:"" in status)
1294+
statusMap := map[string]interface{}{
1295+
"registered": newObj.Status.Registered,
1296+
}
1297+
1298+
// Include error field if:
1299+
// 1. newObj has an error message, OR
1300+
// 2. we're clearing an error (oldObj has error, newObj doesn't)
1301+
if newObj.Status.Error != "" || (oldObj.Status.Error != "" && newObj.Status.Error == "") {
1302+
statusMap["error"] = newObj.Status.Error
1303+
}
1304+
1305+
statusPatch := map[string]interface{}{
1306+
"status": statusMap,
1307+
}
1308+
1309+
patchBytes, err := json.Marshal(statusPatch)
1310+
if err != nil {
1311+
log.Errorf("failed to marshal status patch. err: %v", err)
1312+
return err
1313+
}
1314+
1315+
rawPatch := client.RawPatch(apitypes.MergePatchType, patchBytes)
1316+
1317+
// Try to patch CnsRegisterVolume CR for 3 times
1318+
allowedRetries := 3
1319+
attempt := 0
1320+
for {
1321+
attempt++
1322+
err := cnsOperatorClient.Status().Patch(ctx, oldObj, rawPatch)
1323+
if err != nil && attempt >= allowedRetries {
1324+
log.Errorf("failed to patch CnsRegisterVolume instance %q on namespace %q, Error: %+v",
1325+
oldObj.Name, oldObj.Namespace, err)
1326+
return err
1327+
} else if err == nil {
1328+
log.Debugf("Successfully patched CnsRegisterVolume instance %q on namespace %q",
1329+
oldObj.Name, oldObj.Namespace)
1330+
return nil
1331+
}
1332+
log.Warnf("attempt %d, failed to patch CnsRegisterVolume instance %q on namespace %q with error %+v, "+
1333+
"will retry...", attempt, oldObj.Name, oldObj.Namespace, err)
1334+
time.Sleep(100 * time.Millisecond)
1335+
}
1336+
}

0 commit comments

Comments
 (0)