@@ -21,10 +21,15 @@ import (
2121 "time"
2222
2323 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+ "k8s.io/client-go/kubernetes"
2425 "k8s.io/client-go/rest"
26+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2527 cnsoperatorv1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator"
28+ batchattachv1a1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/cnsnodevmbatchattachment/v1alpha1"
2629 cnsregistervolumev1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/cnsregistervolume/v1alpha1"
2730 cnsunregistervolumev1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/cnsunregistervolume/v1alpha1"
31+ "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common/commonco"
32+ cnsoperatortypes "sigs.k8s.io/vsphere-csi-driver/v3/pkg/syncer/cnsoperator/types"
2833
2934 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger"
3035 k8s "sigs.k8s.io/vsphere-csi-driver/v3/pkg/kubernetes"
@@ -109,3 +114,76 @@ func cleanUpCnsUnregisterVolumeInstances(ctx context.Context, restClientConfig *
109114 }
110115 }
111116}
117+
118+ var (
119+ newClientForGroup = k8s .NewClientForGroup
120+ newForConfig = func (config * rest.Config ) (kubernetes.Interface , error ) {
121+ return kubernetes .NewForConfig (config )
122+ }
123+ )
124+
125+ // cleanupPVCs removes the `CNSPvcFinalizer` from the PVCs in cases
126+ // where the CnsNodeVmBatchAttachment CR gets deleted before removing the finalizer.
127+ // This is EXTREMELY UNLIKELY to happen but still a possibility that has to be addressed.
128+ func cleanupPVCs (ctx context.Context , config rest.Config ) {
129+ log := logger .GetLogger (ctx )
130+
131+ pvcList := commonco .ContainerOrchestratorUtility .ListPVCs (ctx , "" )
132+ if len (pvcList ) == 0 {
133+ log .Info ("no PVCs found. Exiting..." )
134+ return
135+ }
136+
137+ // map to hold all the PVCs that have the finalizer added by the batch attach reconciler
138+ pvcMap := make (map [string ]map [string ]struct {})
139+ for _ , pvc := range pvcList {
140+ // Check if PVC has the CNS finalizer in metadata.finalizers
141+ if ! controllerutil .ContainsFinalizer (pvc , cnsoperatortypes .CNSPvcFinalizer ) {
142+ // not a PVC that is attached to or being attached to a VM. Can be ignored.
143+ continue
144+ }
145+
146+ if _ , ok := pvcMap [pvc .Namespace ]; ! ok {
147+ pvcMap [pvc .Namespace ] = map [string ]struct {}{}
148+ }
149+ pvcMap [pvc.Namespace ][pvc.Name ] = struct {}{}
150+ }
151+
152+ cnsClient , err := newClientForGroup (ctx , & config , cnsoperatorv1alpha1 .GroupName )
153+ if err != nil {
154+ log .Error ("failed to create cns operator client" )
155+ return
156+ }
157+
158+ batchAttachList := batchattachv1a1.CnsNodeVMBatchAttachmentList {}
159+ err = cnsClient .List (ctx , & batchAttachList )
160+ if err != nil {
161+ log .With ("kind" , batchAttachList .Kind ).Error ("listing failed" )
162+ return
163+ }
164+
165+ for _ , cr := range batchAttachList .Items {
166+ for _ , vol := range cr .Spec .Volumes {
167+ // Any PVCs that are still in the spec can be safely ignored to be processed by the reconciler.
168+ delete (pvcMap [cr .Namespace ], vol .PersistentVolumeClaim .ClaimName )
169+ }
170+ }
171+
172+ c , err := newForConfig (& config )
173+ if err != nil {
174+ log .Error ("failed to create core API client" )
175+ return
176+ }
177+
178+ // Remove the finalizer for the remaining PVCs
179+ for namespace , pvcs := range pvcMap {
180+ for name := range pvcs {
181+ err := k8s .RemoveFinalizerFromPVC (ctx , c , name , namespace , cnsoperatortypes .CNSPvcFinalizer )
182+ if err != nil {
183+ log .With ("name" , name ).With ("namespace" , namespace ).
184+ Error ("failed to remove the finalizer" )
185+ continue
186+ }
187+ }
188+ }
189+ }
0 commit comments