@@ -21,9 +21,11 @@ import (
2121 "encoding/json"
2222 "errors"
2323 "fmt"
24+ "net/http"
2425 "reflect"
2526 "strconv"
2627 "strings"
28+ "sync"
2729 "time"
2830
2931 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v7"
@@ -36,13 +38,18 @@ import (
3638
3739 grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
3840 corev1 "k8s.io/api/core/v1"
41+ v1 "k8s.io/api/core/v1"
3942 apierrors "k8s.io/apimachinery/pkg/api/errors"
4043 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
44+ "k8s.io/apimachinery/pkg/labels"
4145 k8stypes "k8s.io/apimachinery/pkg/types"
4246 "k8s.io/apimachinery/pkg/util/wait"
47+ "k8s.io/client-go/informers"
4348 clientset "k8s.io/client-go/kubernetes"
4449 "k8s.io/client-go/kubernetes/scheme"
4550 clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
51+ corelisters "k8s.io/client-go/listers/core/v1"
52+ "k8s.io/client-go/tools/cache"
4653 "k8s.io/client-go/tools/record"
4754 "k8s.io/klog/v2"
4855 "k8s.io/kubernetes/pkg/volume/util/hostutil"
@@ -52,7 +59,6 @@ import (
5259 "k8s.io/apimachinery/pkg/runtime/schema"
5360 "k8s.io/client-go/metadata"
5461 "k8s.io/client-go/metadata/metadatainformer"
55- "k8s.io/client-go/tools/cache"
5662
5763 consts "sigs.k8s.io/azuredisk-csi-driver/pkg/azureconstants"
5864 "sigs.k8s.io/azuredisk-csi-driver/pkg/azureutils"
@@ -169,6 +175,21 @@ type Driver struct {
169175 nodeLister cache.GenericLister
170176 nodeInformerSynced cache.InformerSynced
171177 nodeInformerFactory metadatainformer.SharedInformerFactory
178+ // maximum number of data disks attachable to this node, lazily computed in NodeGetInfo
179+ maxDataDiskCount int64
180+ // HTTP client for wireserver calls
181+ httpClient * http.Client
182+ // in-process batcher to coalesce concurrent QAD attach/detach requests from node RPC callers
183+ qadBatcher * qadDiskBatcher
184+ // informer factory and PV lister for cached API access
185+ informerFactory informers.SharedInformerFactory
186+ pvLister corelisters.PersistentVolumeLister
187+ pvListerSynced cache.InformerSynced
188+ // owning AKS cluster ARM ID, resolved once from node resource group tags and reused thereafter
189+ clusterResourceID string
190+ clusterResourceIDLock sync.Mutex
191+ // interval between Azure async operation polls; defaults to 5s when unset
192+ pollInterval time.Duration
172193}
173194
174195// NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version &
@@ -268,6 +289,16 @@ func NewDriver(options *DriverOptions) *Driver {
268289 }
269290 driver .kubeClient = kubeClient
270291
292+ if driver .NodeID != "" {
293+ // Initialize HTTP client for wireserver calls (node component only)
294+ driver .httpClient = & http.Client {
295+ Timeout : 30 * time .Second ,
296+ }
297+ // Initialize QAD batcher; batch size is resolved lazily from d.maxDataDiskCount
298+ // which is populated on the first NodeGetInfo call.
299+ driver .qadBatcher = newQADDiskBatcher (1000 * time .Millisecond )
300+ }
301+
271302 cloud , err := azureutils .GetCloudProviderFromClient (context .Background (), kubeClient , driver .cloudConfigSecretName , driver .cloudConfigSecretNamespace ,
272303 userAgent , driver .allowEmptyCloudConfig , driver .enableTrafficManager , driver .enableMinimumRetryAfter , driver .trafficManagerPort )
273304 if err != nil {
@@ -421,6 +452,13 @@ func NewDriver(options *DriverOptions) *Driver {
421452 csi .NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER ,
422453 })
423454
455+ if kubeClient != nil {
456+ driver .informerFactory = informers .NewSharedInformerFactory (kubeClient , 10 * time .Minute )
457+ pvInformer := driver .informerFactory .Core ().V1 ().PersistentVolumes ()
458+ driver .pvLister = pvInformer .Lister ()
459+ driver .pvListerSynced = pvInformer .Informer ().HasSynced
460+ }
461+
424462 if kubeClient != nil && driver .removeNotReadyTaint && driver .NodeID != "" {
425463 // Remove taint from node to indicate driver startup success
426464 // This is done at the last possible moment to prevent race conditions or false positive removals
@@ -477,10 +515,25 @@ func (d *Driver) Run(ctx context.Context) error {
477515 klog .V (2 ).Infof ("started metadata node informer for GetNodeInfoFromLabels caching" )
478516 }
479517
518+ // Start informer factory if initialized
519+ if d .informerFactory != nil {
520+ d .informerFactory .Start (ctx .Done ())
521+ if ! cache .WaitForCacheSync (ctx .Done (), d .pvListerSynced ) {
522+ klog .Errorf ("failed to sync PV informer cache" )
523+ } else {
524+ klog .V (2 ).Infof ("PV informer cache synced successfully" )
525+ }
526+ }
527+
480528 go func () {
481529 //graceful shutdown
482530 <- ctx .Done ()
483531
532+ // Shutdown informer factory
533+ if d .informerFactory != nil {
534+ d .informerFactory .Shutdown ()
535+ }
536+
484537 // Stop migration monitor if it exists
485538 if d .migrationMonitor != nil {
486539 d .migrationMonitor .Stop ()
@@ -764,7 +817,39 @@ func GetNodeInfoFromNodeLister(nodeName string, nodeLister cache.GenericLister)
764817 return zone , instanceType , nil
765818}
766819
767- // GetNodeInfoFromLabels gets zone, instanceType from node labels via the kubeClient API server.
820+ func (d * Driver ) getPVFromDiskURI (ctx context.Context , diskURI string ) (* v1.PersistentVolume , error ) {
821+ klog .Infof ("Looking for PV with handle %s" , diskURI )
822+
823+ // Use cached PV lister if available
824+ if d .pvLister != nil {
825+ pvs , err := d .pvLister .List (labels .Everything ())
826+ if err != nil {
827+ return nil , fmt .Errorf ("failed to list PersistentVolumes from cache: %v" , err )
828+ }
829+ for _ , pv := range pvs {
830+ if pv .Spec .CSI != nil && pv .Spec .CSI .VolumeHandle == diskURI {
831+ klog .Infof ("Found PV %s with handle %s (from cache)" , pv .Name , diskURI )
832+ return pv , nil
833+ }
834+ }
835+ return nil , fmt .Errorf ("cannot find PV with diskURI(%s)" , diskURI )
836+ }
837+
838+ // Fallback to direct API call if lister is not initialized
839+ pvList , err := d .kubeClient .CoreV1 ().PersistentVolumes ().List (ctx , metav1.ListOptions {})
840+ if err != nil {
841+ return nil , fmt .Errorf ("failed to list PersistentVolumes: %v" , err )
842+ }
843+ for _ , pv := range pvList .Items {
844+ if pv .Spec .CSI != nil && pv .Spec .CSI .VolumeHandle == diskURI {
845+ klog .Infof ("Found PV %s with handle %s" , pv .Name , diskURI )
846+ return & pv , nil
847+ }
848+ }
849+ return nil , fmt .Errorf ("cannot find PV with diskURI(%s)" , diskURI )
850+ }
851+
852+ // getNodeInfoFromLabels get zone, instanceType from node labels
768853func GetNodeInfoFromLabels (ctx context.Context , nodeName string , kubeClient clientset.Interface ) (string , string , error ) {
769854 if kubeClient == nil || kubeClient .CoreV1 () == nil {
770855 return "" , "" , fmt .Errorf ("kubeClient is nil" )
0 commit comments