@@ -1173,10 +1173,12 @@ func validateAndFixPVVolumeMode(ctx context.Context, k8sclient clientset.Interfa
11731173func 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