Skip to content

Commit 5aee0ff

Browse files
authored
Merge pull request #3767 from kubernetes-sigs/nearora/qad_poc_v2
feat: Initial changes to support QAD workflow
2 parents 393e315 + 30f9633 commit 5aee0ff

10 files changed

Lines changed: 1634 additions & 40 deletions

File tree

charts/latest/azuredisk-csi-driver/templates/rbac-csi-azuredisk-node.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ rules:
1717
- apiGroups: ["storage.k8s.io"]
1818
resources: ["volumeattachments"]
1919
verbs: ["get", "list", "watch"]
20+
- apiGroups: [""]
21+
resources: ["persistentvolumes"]
22+
verbs: ["get", "list", "watch", "patch", "update"]
2023
---
2124
kind: ClusterRoleBinding
2225
apiVersion: rbac.authorization.k8s.io/v1

pkg/azureconstants/azure_constants.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const (
7171
SubscriptionIDField = "subscriptionid"
7272
ResourceGroupField = "resourcegroup"
7373
DataAccessAuthModeField = "dataaccessauthmode"
74+
QADEnabledField = "qadenabled"
7475
ResourceNotFound = "ResourceNotFound"
7576
SkuNameField = "skuname"
7677
SourceDiskSearchMaxDepth = 10
@@ -111,6 +112,10 @@ const (
111112
TagValueDelimiterField = "tagvaluedelimiter"
112113
AzureDiskDriverTag = "kubernetes-azure-dd"
113114
InstantAccessDurationMinutes = "instantaccessdurationminutes"
115+
QADWireserverEndpoint = "http://168.63.129.16/vmservice/diskstate"
116+
AttachSequenceAnnotation = "azuredisk.csi.azure.com/attach-sequence"
117+
BlobURLAnnotation = "azuredisk.csi.azure.com/blob-url"
118+
ClaimIdentifierAnnotation = "azuredisk.csi.azure.com/claim-identifier"
114119
)
115120

116121
var (

pkg/azuredisk/azuredisk.go

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
768853
func 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

Comments
 (0)