• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / blob-csi-driver / 9401119755

06 Jun 2024 12:46PM UTC coverage: 71.986%. Remained the same
9401119755

push

github

web-flow
Merge pull request #1412 from k8s-infra-cherrypick-robot/cherry-pick-1409-to-release-1.23

[release-1.23] feat: upgrade to azcopy v10.25.0 for volume clone feature

2048 of 2845 relevant lines covered (71.99%)

6.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.72
/pkg/blob/blob.go
1
/*
2
Copyright 2019 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package blob
18

19
import (
20
        "context"
21
        "errors"
22
        "flag"
23
        "fmt"
24
        "os"
25
        "strconv"
26
        "strings"
27
        "sync"
28
        "time"
29

30
        "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-09-01/storage"
31
        azstorage "github.com/Azure/azure-sdk-for-go/storage"
32
        az "github.com/Azure/go-autorest/autorest/azure"
33
        "github.com/container-storage-interface/spec/lib/go/csi"
34
        "github.com/pborman/uuid"
35
        "google.golang.org/grpc"
36
        v1 "k8s.io/api/core/v1"
37
        apierror "k8s.io/apimachinery/pkg/api/errors"
38
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39
        "k8s.io/client-go/kubernetes"
40
        "k8s.io/klog/v2"
41
        k8sutil "k8s.io/kubernetes/pkg/volume/util"
42
        mount "k8s.io/mount-utils"
43
        utilexec "k8s.io/utils/exec"
44

45
        csicommon "sigs.k8s.io/blob-csi-driver/pkg/csi-common"
46
        "sigs.k8s.io/blob-csi-driver/pkg/util"
47
        azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
48
        "sigs.k8s.io/cloud-provider-azure/pkg/provider"
49
        azure "sigs.k8s.io/cloud-provider-azure/pkg/provider"
50
)
51

52
const (
53
        // DefaultDriverName holds the name of the csi-driver
54
        DefaultDriverName              = "blob.csi.azure.com"
55
        blobCSIDriverName              = "blob_csi_driver"
56
        separator                      = "#"
57
        volumeIDTemplate               = "%s#%s#%s#%s#%s#%s"
58
        secretNameTemplate             = "azure-storage-account-%s-secret"
59
        serverNameField                = "server"
60
        storageEndpointSuffixField     = "storageendpointsuffix"
61
        tagsField                      = "tags"
62
        matchTagsField                 = "matchtags"
63
        protocolField                  = "protocol"
64
        accountNameField               = "accountname"
65
        accountKeyField                = "accountkey"
66
        storageAccountField            = "storageaccount"
67
        storageAccountTypeField        = "storageaccounttype"
68
        skuNameField                   = "skuname"
69
        subscriptionIDField            = "subscriptionid"
70
        resourceGroupField             = "resourcegroup"
71
        locationField                  = "location"
72
        secretNameField                = "secretname"
73
        secretNamespaceField           = "secretnamespace"
74
        containerNameField             = "containername"
75
        containerNamePrefixField       = "containernameprefix"
76
        storeAccountKeyField           = "storeaccountkey"
77
        getLatestAccountKeyField       = "getlatestaccountkey"
78
        isHnsEnabledField              = "ishnsenabled"
79
        softDeleteBlobsField           = "softdeleteblobs"
80
        softDeleteContainersField      = "softdeletecontainers"
81
        enableBlobVersioningField      = "enableblobversioning"
82
        getAccountKeyFromSecretField   = "getaccountkeyfromsecret"
83
        storageSPNClientIDField        = "azurestoragespnclientid"
84
        storageSPNTenantIDField        = "azurestoragespntenantid"
85
        storageAuthTypeField           = "azurestorageauthtype"
86
        storageIentityClientIDField    = "azurestorageidentityclientid"
87
        storageIdentityObjectIDField   = "azurestorageidentityobjectid"
88
        storageIdentityResourceIDField = "azurestorageidentityresourceid"
89
        msiEndpointField               = "msiendpoint"
90
        storageAADEndpointField        = "azurestorageaadendpoint"
91
        keyVaultURLField               = "keyvaulturl"
92
        keyVaultSecretNameField        = "keyvaultsecretname"
93
        keyVaultSecretVersionField     = "keyvaultsecretversion"
94
        storageAccountNameField        = "storageaccountname"
95
        allowBlobPublicAccessField     = "allowblobpublicaccess"
96
        requireInfraEncryptionField    = "requireinfraencryption"
97
        ephemeralField                 = "csi.storage.k8s.io/ephemeral"
98
        podNamespaceField              = "csi.storage.k8s.io/pod.namespace"
99
        serviceAccountTokenField       = "csi.storage.k8s.io/serviceAccount.tokens"
100
        clientIDField                  = "clientID"
101
        tenantIDField                  = "tenantID"
102
        mountOptionsField              = "mountoptions"
103
        falseValue                     = "false"
104
        trueValue                      = "true"
105
        defaultSecretAccountName       = "azurestorageaccountname"
106
        defaultSecretAccountKey        = "azurestorageaccountkey"
107
        accountSasTokenField           = "azurestorageaccountsastoken"
108
        msiSecretField                 = "msisecret"
109
        storageSPNClientSecretField    = "azurestoragespnclientsecret"
110
        Fuse                           = "fuse"
111
        Fuse2                          = "fuse2"
112
        NFS                            = "nfs"
113
        AZNFS                          = "aznfs"
114
        NFSv3                          = "nfsv3"
115
        vnetResourceGroupField         = "vnetresourcegroup"
116
        vnetNameField                  = "vnetname"
117
        subnetNameField                = "subnetname"
118
        accessTierField                = "accesstier"
119
        networkEndpointTypeField       = "networkendpointtype"
120
        mountPermissionsField          = "mountpermissions"
121
        useDataPlaneAPIField           = "usedataplaneapi"
122

123
        // See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
124
        containerNameMinLength = 3
125
        containerNameMaxLength = 63
126

127
        accountNotProvisioned                   = "StorageAccountIsNotProvisioned"
128
        tooManyRequests                         = "TooManyRequests"
129
        clientThrottled                         = "client throttled"
130
        containerBeingDeletedDataplaneAPIError  = "ContainerBeingDeleted"
131
        containerBeingDeletedManagementAPIError = "container is being deleted"
132
        statusCodeNotFound                      = "StatusCode=404"
133
        httpCodeNotFound                        = "HTTPStatusCode: 404"
134

135
        // containerMaxSize is the max size of the blob container. See https://docs.microsoft.com/en-us/azure/storage/blobs/scalability-targets#scale-targets-for-blob-storage
136
        containerMaxSize = 100 * util.TiB
137

138
        subnetTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s"
139

140
        defaultNamespace = "default"
141

142
        pvcNameKey           = "csi.storage.k8s.io/pvc/name"
143
        pvcNamespaceKey      = "csi.storage.k8s.io/pvc/namespace"
144
        pvNameKey            = "csi.storage.k8s.io/pv/name"
145
        pvcNameMetadata      = "${pvc.metadata.name}"
146
        pvcNamespaceMetadata = "${pvc.metadata.namespace}"
147
        pvNameMetadata       = "${pv.metadata.name}"
148

149
        VolumeID = "volumeid"
150

151
        defaultStorageEndPointSuffix = "core.windows.net"
152
)
153

154
var (
155
        supportedProtocolList = []string{Fuse, Fuse2, NFS, AZNFS}
156
        retriableErrors       = []string{accountNotProvisioned, tooManyRequests, statusCodeNotFound, containerBeingDeletedDataplaneAPIError, containerBeingDeletedManagementAPIError, clientThrottled}
157
)
158

159
// DriverOptions defines driver parameters specified in driver deployment
160
type DriverOptions struct {
161
        NodeID                                 string
162
        DriverName                             string
163
        BlobfuseProxyEndpoint                  string
164
        EnableBlobfuseProxy                    bool
165
        BlobfuseProxyConnTimout                int
166
        EnableBlobMockMount                    bool
167
        AllowInlineVolumeKeyAccessWithIdentity bool
168
        EnableGetVolumeStats                   bool
169
        AppendTimeStampInCacheDir              bool
170
        AppendMountErrorHelpLink               bool
171
        MountPermissions                       uint64
172
        EnableAznfsMount                       bool
173
        VolStatsCacheExpireInMinutes           int
174
        SasTokenExpirationMinutes              int
175
        WaitForAzCopyTimeoutMinutes            int
176
}
177

178
func (option *DriverOptions) AddFlags() {
1✔
179
        flag.StringVar(&option.BlobfuseProxyEndpoint, "blobfuse-proxy-endpoint", "unix://tmp/blobfuse-proxy.sock", "blobfuse-proxy endpoint")
1✔
180
        flag.StringVar(&option.NodeID, "nodeid", "", "node id")
1✔
181
        flag.StringVar(&option.DriverName, "drivername", DefaultDriverName, "name of the driver")
1✔
182
        flag.BoolVar(&option.EnableBlobfuseProxy, "enable-blobfuse-proxy", false, "using blobfuse proxy for mounts")
1✔
183
        flag.IntVar(&option.BlobfuseProxyConnTimout, "blobfuse-proxy-connect-timeout", 5, "blobfuse proxy connection timeout(seconds)")
1✔
184
        flag.BoolVar(&option.EnableBlobMockMount, "enable-blob-mock-mount", false, "enable mock mount(only for testing)")
1✔
185
        flag.BoolVar(&option.EnableGetVolumeStats, "enable-get-volume-stats", false, "allow GET_VOLUME_STATS on agent node")
1✔
186
        flag.BoolVar(&option.AppendTimeStampInCacheDir, "append-timestamp-cache-dir", false, "append timestamp into cache directory on agent node")
1✔
187
        flag.Uint64Var(&option.MountPermissions, "mount-permissions", 0777, "mounted folder permissions")
1✔
188
        flag.BoolVar(&option.AllowInlineVolumeKeyAccessWithIdentity, "allow-inline-volume-key-access-with-idenitity", false, "allow accessing storage account key using cluster identity for inline volume")
1✔
189
        flag.BoolVar(&option.AppendMountErrorHelpLink, "append-mount-error-help-link", true, "Whether to include a link for help with mount errors when a mount error occurs.")
1✔
190
        flag.BoolVar(&option.EnableAznfsMount, "enable-aznfs-mount", false, "replace nfs mount with aznfs mount")
1✔
191
        flag.IntVar(&option.VolStatsCacheExpireInMinutes, "vol-stats-cache-expire-in-minutes", 10, "The cache expire time in minutes for volume stats cache")
1✔
192
        flag.IntVar(&option.SasTokenExpirationMinutes, "sas-token-expiration-minutes", 1440, "sas token expiration minutes during volume cloning")
1✔
193
        flag.IntVar(&option.WaitForAzCopyTimeoutMinutes, "wait-for-azcopy-timeout-minutes", 18, "timeout in minutes for waiting for azcopy to finish")
1✔
194
}
1✔
195

196
// Driver implements all interfaces of CSI drivers
197
type Driver struct {
198
        csicommon.CSIDriver
199

200
        cloud                 *azure.Cloud
201
        KubeClient            kubernetes.Interface
202
        blobfuseProxyEndpoint string
203
        // enableBlobMockMount is only for testing, DO NOT set as true in non-testing scenario
204
        enableBlobMockMount                    bool
205
        enableBlobfuseProxy                    bool
206
        enableGetVolumeStats                   bool
207
        allowInlineVolumeKeyAccessWithIdentity bool
208
        appendTimeStampInCacheDir              bool
209
        appendMountErrorHelpLink               bool
210
        blobfuseProxyConnTimout                int
211
        mountPermissions                       uint64
212
        enableAznfsMount                       bool
213
        mounter                                *mount.SafeFormatAndMount
214
        volLockMap                             *util.LockMap
215
        // A map storing all volumes with ongoing operations so that additional operations
216
        // for that same volume (as defined by VolumeID) return an Aborted error
217
        volumeLocks *volumeLocks
218
        // only for nfs feature
219
        subnetLockMap *util.LockMap
220
        // a map storing all volumes created by this driver <volumeName, accountName>
221
        volMap sync.Map
222
        // a timed cache storing all volumeIDs and storage accounts that are using data plane API
223
        dataPlaneAPIVolCache azcache.Resource
224
        // a timed cache storing account search history (solve account list throttling issue)
225
        accountSearchCache azcache.Resource
226
        // a timed cache storing volume stats <volumeID, volumeStats>
227
        volStatsCache azcache.Resource
228
        // a timed cache storing account which should use sastoken for azcopy based volume cloning
229
        azcopySasTokenCache azcache.Resource
230
        // sas expiry time for azcopy in volume clone
231
        sasTokenExpirationMinutes int
232
        // timeout in minutes for waiting for azcopy to finish
233
        waitForAzCopyTimeoutMinutes int
234
        // azcopy for provide exec mock for ut
235
        azcopy *util.Azcopy
236
}
237

238
// NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version &
239
// does not support optional driver plugin info manifest field. Refer to CSI spec for more details.
240
func NewDriver(options *DriverOptions, kubeClient kubernetes.Interface, cloud *provider.Cloud) *Driver {
124✔
241
        var err error
124✔
242
        d := Driver{
124✔
243
                volLockMap:                             util.NewLockMap(),
124✔
244
                subnetLockMap:                          util.NewLockMap(),
124✔
245
                volumeLocks:                            newVolumeLocks(),
124✔
246
                blobfuseProxyEndpoint:                  options.BlobfuseProxyEndpoint,
124✔
247
                enableBlobfuseProxy:                    options.EnableBlobfuseProxy,
124✔
248
                allowInlineVolumeKeyAccessWithIdentity: options.AllowInlineVolumeKeyAccessWithIdentity,
124✔
249
                blobfuseProxyConnTimout:                options.BlobfuseProxyConnTimout,
124✔
250
                enableBlobMockMount:                    options.EnableBlobMockMount,
124✔
251
                enableGetVolumeStats:                   options.EnableGetVolumeStats,
124✔
252
                appendMountErrorHelpLink:               options.AppendMountErrorHelpLink,
124✔
253
                mountPermissions:                       options.MountPermissions,
124✔
254
                enableAznfsMount:                       options.EnableAznfsMount,
124✔
255
                sasTokenExpirationMinutes:              options.SasTokenExpirationMinutes,
124✔
256
                waitForAzCopyTimeoutMinutes:            options.WaitForAzCopyTimeoutMinutes,
124✔
257
                azcopy:                                 &util.Azcopy{},
124✔
258
                KubeClient:                             kubeClient,
124✔
259
                cloud:                                  cloud,
124✔
260
        }
124✔
261
        d.Name = options.DriverName
124✔
262
        d.Version = driverVersion
124✔
263
        d.NodeID = options.NodeID
124✔
264

124✔
265
        getter := func(key string) (interface{}, error) { return nil, nil }
148✔
266
        if d.accountSearchCache, err = azcache.NewTimedCache(time.Minute, getter, false); err != nil {
124✔
267
                klog.Fatalf("%v", err)
×
268
        }
×
269
        if d.dataPlaneAPIVolCache, err = azcache.NewTimedCache(24*30*time.Hour, getter, false); err != nil {
124✔
270
                klog.Fatalf("%v", err)
×
271
        }
×
272
        if d.azcopySasTokenCache, err = azcache.NewTimedCache(15*time.Minute, getter, false); err != nil {
124✔
273
                klog.Fatalf("%v", err)
×
274
        }
×
275

276
        if options.VolStatsCacheExpireInMinutes <= 0 {
248✔
277
                options.VolStatsCacheExpireInMinutes = 10 // default expire in 10 minutes
124✔
278
        }
124✔
279
        if d.volStatsCache, err = azcache.NewTimedCache(time.Duration(options.VolStatsCacheExpireInMinutes)*time.Minute, getter, false); err != nil {
124✔
280
                klog.Fatalf("%v", err)
×
281
        }
×
282
        d.mounter = &mount.SafeFormatAndMount{
124✔
283
                Interface: mount.New(""),
124✔
284
                Exec:      utilexec.New(),
124✔
285
        }
124✔
286

124✔
287
        // Initialize default library driver
124✔
288
        d.AddControllerServiceCapabilities(
124✔
289
                []csi.ControllerServiceCapability_RPC_Type{
124✔
290
                        csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
124✔
291
                        //csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
124✔
292
                        //csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
124✔
293
                        csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
124✔
294
                        csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
124✔
295
                        csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
124✔
296
                })
124✔
297
        d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{
124✔
298
                csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
124✔
299
                csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
124✔
300
                csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
124✔
301
                csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
124✔
302
                csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
124✔
303
                csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER,
124✔
304
                csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
124✔
305
        })
124✔
306

124✔
307
        nodeCap := []csi.NodeServiceCapability_RPC_Type{
124✔
308
                csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
124✔
309
                csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
124✔
310
        }
124✔
311
        if d.enableGetVolumeStats {
124✔
312
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
×
313
        }
×
314
        d.AddNodeServiceCapabilities(nodeCap)
124✔
315

124✔
316
        return &d
124✔
317
}
318

319
// Run driver initialization
320
func (d *Driver) Run(ctx context.Context, endpoint string) error {
2✔
321
        versionMeta, err := GetVersionYAML(d.Name)
2✔
322
        if err != nil {
2✔
323
                klog.Fatalf("%v", err)
×
324
        }
×
325
        klog.Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta)
2✔
326
        grpcInterceptor := grpc.UnaryInterceptor(csicommon.LogGRPC)
2✔
327
        opts := []grpc.ServerOption{
2✔
328
                grpcInterceptor,
2✔
329
        }
2✔
330
        s := grpc.NewServer(opts...)
2✔
331
        csi.RegisterIdentityServer(s, d)
2✔
332
        csi.RegisterControllerServer(s, d)
2✔
333
        csi.RegisterNodeServer(s, d)
2✔
334

2✔
335
        go func() {
4✔
336
                //graceful shutdown
2✔
337
                <-ctx.Done()
2✔
338
                s.GracefulStop()
2✔
339
        }()
2✔
340
        // Driver d act as IdentityServer, ControllerServer and NodeServer
341
        listener, err := csicommon.Listen(ctx, endpoint)
2✔
342
        if err != nil {
2✔
343
                klog.Fatalf("failed to listen to endpoint, error: %v", err)
×
344
        }
×
345
        err = s.Serve(listener)
2✔
346
        if errors.Is(err, grpc.ErrServerStopped) {
2✔
347
                klog.Infof("gRPC server stopped serving")
×
348
                return nil
×
349
        }
×
350
        return err
2✔
351
}
352

353
// GetContainerInfo get container info according to volume id
354
// the format of VolumeId is: rg#accountName#containerName#uuid#secretNamespace#subsID
355
//
356
// e.g.
357
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#"
358
// output: rg, f5713de20cde511e8ba4900, containerName, "" , ""
359
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#"
360
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, ""
361
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#subsID"
362
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, subsID
363
func GetContainerInfo(id string) (string, string, string, string, string, error) {
39✔
364
        segments := strings.Split(id, separator)
39✔
365
        if len(segments) < 3 {
48✔
366
                return "", "", "", "", "", fmt.Errorf("error parsing volume id: %q, should at least contain two #", id)
9✔
367
        }
9✔
368
        var secretNamespace, subsID string
30✔
369
        if len(segments) > 4 {
36✔
370
                secretNamespace = segments[4]
6✔
371
        }
6✔
372
        if len(segments) > 5 {
33✔
373
                subsID = segments[5]
3✔
374
        }
3✔
375
        return segments[0], segments[1], segments[2], secretNamespace, subsID, nil
30✔
376
}
377

378
// A container name must be a valid DNS name, conforming to the following naming rules:
379
//  1. Container names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
380
//  2. Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names.
381
//  3. All letters in a container name must be lowercase.
382
//  4. Container names must be from 3 through 63 characters long.
383
//
384
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
385
func getValidContainerName(volumeName, protocol string) string {
5✔
386
        containerName := strings.ToLower(volumeName)
5✔
387
        if len(containerName) > containerNameMaxLength {
6✔
388
                containerName = containerName[0:containerNameMaxLength]
1✔
389
        }
1✔
390
        if !checkContainerNameBeginAndEnd(containerName) || len(containerName) < containerNameMinLength {
5✔
391
                // now we set as 63 for maximum container name length
×
392
                // todo: get cluster name
×
393
                containerName = k8sutil.GenerateVolumeName(fmt.Sprintf("pvc-%s", protocol), uuid.NewUUID().String(), 63)
×
394
                klog.Warningf("requested volume name (%s) is invalid, regenerated as (%q)", volumeName, containerName)
×
395
        }
×
396
        return strings.Replace(containerName, "--", "-", -1)
5✔
397
}
398

399
func checkContainerNameBeginAndEnd(containerName string) bool {
11✔
400
        length := len(containerName)
11✔
401
        if (('a' <= containerName[0] && containerName[0] <= 'z') ||
11✔
402
                ('0' <= containerName[0] && containerName[0] <= '9')) &&
11✔
403
                (('a' <= containerName[length-1] && containerName[length-1] <= 'z') ||
11✔
404
                        ('0' <= containerName[length-1] && containerName[length-1] <= '9')) {
20✔
405
                return true
9✔
406
        }
9✔
407

408
        return false
2✔
409
}
410

411
// isSASToken checks if the key contains the patterns.
412
// SAS token format could refer to https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
413
func isSASToken(key string) bool {
3✔
414
        return strings.HasPrefix(key, "?")
3✔
415
}
3✔
416

417
// GetAuthEnv return <accountName, containerName, authEnv, error>
418
func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attrib, secrets map[string]string) (string, string, string, string, []string, error) {
10✔
419
        rgName, accountName, containerName, secretNamespace, _, err := GetContainerInfo(volumeID)
10✔
420
        if err != nil {
12✔
421
                // ignore volumeID parsing error
2✔
422
                klog.V(2).Infof("parsing volumeID(%s) return with error: %v", volumeID, err)
2✔
423
                err = nil
2✔
424
        }
2✔
425

426
        var (
10✔
427
                subsID                  string
10✔
428
                accountKey              string
10✔
429
                accountSasToken         string
10✔
430
                msiSecret               string
10✔
431
                storageSPNClientSecret  string
10✔
432
                storageSPNClientID      string
10✔
433
                storageSPNTenantID      string
10✔
434
                secretName              string
10✔
435
                pvcNamespace            string
10✔
436
                keyVaultURL             string
10✔
437
                keyVaultSecretName      string
10✔
438
                keyVaultSecretVersion   string
10✔
439
                azureStorageAuthType    string
10✔
440
                authEnv                 []string
10✔
441
                getAccountKeyFromSecret bool
10✔
442
                getLatestAccountKey     bool
10✔
443
                clientID                string
10✔
444
                tenantID                string
10✔
445
                serviceAccountToken     string
10✔
446
        )
10✔
447

10✔
448
        for k, v := range attrib {
36✔
449
                switch strings.ToLower(k) {
26✔
450
                case subscriptionIDField:
1✔
451
                        subsID = v
1✔
452
                case resourceGroupField:
×
453
                        rgName = v
×
454
                case containerNameField:
3✔
455
                        containerName = v
3✔
456
                case keyVaultURLField:
1✔
457
                        keyVaultURL = v
1✔
458
                case keyVaultSecretNameField:
1✔
459
                        keyVaultSecretName = v
1✔
460
                case keyVaultSecretVersionField:
1✔
461
                        keyVaultSecretVersion = v
1✔
462
                case storageAccountField:
2✔
463
                        accountName = v
2✔
464
                case storageAccountNameField: // for compatibility
1✔
465
                        accountName = v
1✔
466
                case secretNameField:
1✔
467
                        secretName = v
1✔
468
                case secretNamespaceField:
1✔
469
                        secretNamespace = v
1✔
470
                case pvcNamespaceKey:
1✔
471
                        pvcNamespace = v
1✔
472
                case getAccountKeyFromSecretField:
1✔
473
                        getAccountKeyFromSecret = strings.EqualFold(v, trueValue)
1✔
474
                case storageAuthTypeField:
×
475
                        azureStorageAuthType = v
×
476
                        authEnv = append(authEnv, "AZURE_STORAGE_AUTH_TYPE="+v)
×
477
                case storageIentityClientIDField:
1✔
478
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_CLIENT_ID="+v)
1✔
479
                case storageIdentityObjectIDField:
1✔
480
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_OBJECT_ID="+v)
1✔
481
                case storageIdentityResourceIDField:
1✔
482
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_RESOURCE_ID="+v)
1✔
483
                case msiEndpointField:
1✔
484
                        authEnv = append(authEnv, "MSI_ENDPOINT="+v)
1✔
485
                case storageSPNClientIDField:
1✔
486
                        storageSPNClientID = v
1✔
487
                case storageSPNTenantIDField:
1✔
488
                        storageSPNTenantID = v
1✔
489
                case storageAADEndpointField:
1✔
490
                        authEnv = append(authEnv, "AZURE_STORAGE_AAD_ENDPOINT="+v)
1✔
491
                case getLatestAccountKeyField:
1✔
492
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
493
                                return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
494
                        }
1✔
495
                case strings.ToLower(clientIDField):
×
496
                        clientID = v
×
497
                case strings.ToLower(tenantIDField):
×
498
                        tenantID = v
×
499
                case strings.ToLower(serviceAccountTokenField):
×
500
                        serviceAccountToken = v
×
501
                }
502
        }
503
        klog.V(2).Infof("volumeID(%s) authEnv: %s", volumeID, authEnv)
9✔
504

9✔
505
        if protocol == NFS {
11✔
506
                // nfs protocol does not need account key, return directly
2✔
507
                return rgName, accountName, accountKey, containerName, authEnv, err
2✔
508
        }
2✔
509

510
        if secretNamespace == "" {
13✔
511
                if pvcNamespace == "" {
12✔
512
                        secretNamespace = defaultNamespace
6✔
513
                } else {
6✔
514
                        secretNamespace = pvcNamespace
×
515
                }
×
516
        }
517

518
        if rgName == "" {
8✔
519
                rgName = d.cloud.ResourceGroup
1✔
520
        }
1✔
521

522
        if tenantID == "" {
14✔
523
                tenantID = d.cloud.TenantID
7✔
524
        }
7✔
525

526
        // if client id is specified, we only use service account token to get account key
527
        if clientID != "" {
7✔
528
                klog.V(2).Infof("clientID(%s) is specified, use service account token to get account key", clientID)
×
529
                if subsID == "" {
×
530
                        subsID = d.cloud.SubscriptionID
×
531
                }
×
532
                accountKey, err := d.cloud.GetStorageAccesskeyFromServiceAccountToken(ctx, subsID, accountName, rgName, clientID, tenantID, serviceAccountToken)
×
533
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
×
534
                return rgName, accountName, accountKey, containerName, authEnv, err
×
535
        }
536

537
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
538
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
539
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
540
        if keyVaultURL != "" {
8✔
541
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
1✔
542
                if err != nil {
2✔
543
                        return rgName, accountName, accountKey, containerName, authEnv, err
1✔
544
                }
1✔
545
                if isSASToken(key) {
×
546
                        accountSasToken = key
×
547
                } else {
×
548
                        accountKey = key
×
549
                }
×
550
        } else {
6✔
551
                if len(secrets) == 0 {
11✔
552
                        if secretName == "" && accountName != "" {
9✔
553
                                secretName = fmt.Sprintf(secretNameTemplate, accountName)
4✔
554
                        }
4✔
555
                        if secretName != "" {
10✔
556
                                // read from k8s secret first
5✔
557
                                var name, spnClientID, spnTenantID string
5✔
558
                                name, accountKey, accountSasToken, msiSecret, storageSPNClientSecret, spnClientID, spnTenantID, err = d.GetInfoFromSecret(ctx, secretName, secretNamespace)
5✔
559
                                if name != "" {
5✔
560
                                        accountName = name
×
561
                                }
×
562
                                if spnClientID != "" {
5✔
563
                                        storageSPNClientID = spnClientID
×
564
                                }
×
565
                                if spnTenantID != "" {
5✔
566
                                        storageSPNTenantID = spnTenantID
×
567
                                }
×
568
                                if err != nil && strings.EqualFold(azureStorageAuthType, "msi") {
5✔
569
                                        klog.V(2).Infof("ignore error(%v) since secret is optional for auth type(%s)", err, azureStorageAuthType)
×
570
                                        err = nil
×
571
                                }
×
572
                                if err != nil && !getAccountKeyFromSecret && (azureStorageAuthType == "" || strings.EqualFold(azureStorageAuthType, "key")) {
10✔
573
                                        klog.V(2).Infof("get account(%s) key from secret(%s, %s) failed with error: %v, use cluster identity to get account key instead",
5✔
574
                                                accountName, secretNamespace, secretName, err)
5✔
575
                                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
5✔
576
                                        if err != nil {
7✔
577
                                                return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
2✔
578
                                        }
2✔
579
                                }
580
                        }
581
                } else {
1✔
582
                        for k, v := range secrets {
8✔
583
                                v = strings.TrimSpace(v)
7✔
584
                                switch strings.ToLower(k) {
7✔
585
                                case accountNameField:
1✔
586
                                        accountName = v
1✔
587
                                case defaultSecretAccountName: // for compatibility with built-in blobfuse plugin
1✔
588
                                        accountName = v
1✔
589
                                case accountKeyField:
1✔
590
                                        accountKey = v
1✔
591
                                case defaultSecretAccountKey: // for compatibility with built-in blobfuse plugin
1✔
592
                                        accountKey = v
1✔
593
                                case accountSasTokenField:
1✔
594
                                        accountSasToken = v
1✔
595
                                case msiSecretField:
1✔
596
                                        msiSecret = v
1✔
597
                                case storageSPNClientSecretField:
1✔
598
                                        storageSPNClientSecret = v
1✔
599
                                case storageSPNClientIDField:
×
600
                                        storageSPNClientID = v
×
601
                                case storageSPNTenantIDField:
×
602
                                        storageSPNTenantID = v
×
603
                                }
604
                        }
605
                }
606
        }
607

608
        if containerName == "" {
4✔
609
                err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
610
        }
×
611

612
        if accountKey != "" {
8✔
613
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
614
        }
4✔
615

616
        if accountSasToken != "" {
5✔
617
                klog.V(2).Infof("accountSasToken is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
618
                authEnv = append(authEnv, "AZURE_STORAGE_SAS_TOKEN="+accountSasToken)
1✔
619
        }
1✔
620

621
        if msiSecret != "" {
5✔
622
                klog.V(2).Infof("msiSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
623
                authEnv = append(authEnv, "MSI_SECRET="+msiSecret)
1✔
624
        }
1✔
625

626
        if storageSPNClientSecret != "" {
5✔
627
                klog.V(2).Infof("storageSPNClientSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
628
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+storageSPNClientSecret)
1✔
629
        }
1✔
630

631
        if storageSPNClientID != "" {
4✔
632
                klog.V(2).Infof("storageSPNClientID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNClientID, accountName, containerName)
×
633
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+storageSPNClientID)
×
634
        }
×
635

636
        if storageSPNTenantID != "" {
4✔
637
                klog.V(2).Infof("storageSPNTenantID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNTenantID, accountName, containerName)
×
638
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+storageSPNTenantID)
×
639
        }
×
640

641
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
642
}
643

644
// GetStorageAccountAndContainer get storage account and container info
645
// returns <accountName, accountKey, accountSasToken, containerName>
646
// only for e2e testing
647
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
3✔
648
        var (
3✔
649
                subsID                string
3✔
650
                accountName           string
3✔
651
                accountKey            string
3✔
652
                accountSasToken       string
3✔
653
                containerName         string
3✔
654
                keyVaultURL           string
3✔
655
                keyVaultSecretName    string
3✔
656
                keyVaultSecretVersion string
3✔
657
                getLatestAccountKey   bool
3✔
658
                err                   error
3✔
659
        )
3✔
660

3✔
661
        for k, v := range attrib {
8✔
662
                switch strings.ToLower(k) {
5✔
663
                case subscriptionIDField:
×
664
                        subsID = v
×
665
                case containerNameField:
1✔
666
                        containerName = v
1✔
667
                case keyVaultURLField:
×
668
                        keyVaultURL = v
×
669
                case keyVaultSecretNameField:
1✔
670
                        keyVaultSecretName = v
1✔
671
                case keyVaultSecretVersionField:
1✔
672
                        keyVaultSecretVersion = v
1✔
673
                case storageAccountField:
×
674
                        accountName = v
×
675
                case storageAccountNameField: // for compatibility
1✔
676
                        accountName = v
1✔
677
                case getLatestAccountKeyField:
1✔
678
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
679
                                return "", "", "", "", fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
680
                        }
1✔
681
                }
682
        }
683

684
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
685
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
686
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
687
        if keyVaultURL != "" {
2✔
688
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
×
689
                if err != nil {
×
690
                        return "", "", "", "", err
×
691
                }
×
692
                if isSASToken(key) {
×
693
                        accountSasToken = key
×
694
                } else {
×
695
                        accountKey = key
×
696
                }
×
697
        } else {
2✔
698
                if len(secrets) == 0 {
4✔
699
                        var rgName string
2✔
700
                        rgName, accountName, containerName, _, _, err = GetContainerInfo(volumeID)
2✔
701
                        if err != nil {
2✔
702
                                return "", "", "", "", err
×
703
                        }
×
704

705
                        if rgName == "" {
2✔
706
                                rgName = d.cloud.ResourceGroup
×
707
                        }
×
708

709
                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
2✔
710
                        if err != nil {
3✔
711
                                return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
1✔
712
                        }
1✔
713
                }
714
        }
715

716
        if containerName == "" {
1✔
717
                return "", "", "", "", fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
718
        }
×
719

720
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
721
}
722

723
func IsCorruptedDir(dir string) bool {
4✔
724
        _, pathErr := mount.PathExists(dir)
4✔
725
        return pathErr != nil && mount.IsCorruptedMnt(pathErr)
4✔
726
}
4✔
727

728
func isRetriableError(err error) bool {
5✔
729
        if err != nil {
9✔
730
                for _, v := range retriableErrors {
19✔
731
                        if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(v)) {
18✔
732
                                return true
3✔
733
                        }
3✔
734
                }
735
        }
736
        return false
2✔
737
}
738

739
func isSupportedProtocol(protocol string) bool {
18✔
740
        if protocol == "" {
19✔
741
                return true
1✔
742
        }
1✔
743
        for _, v := range supportedProtocolList {
44✔
744
                if protocol == v || protocol == NFSv3 {
42✔
745
                        return true
15✔
746
                }
15✔
747
        }
748
        return false
2✔
749
}
750

751
func isSupportedAccessTier(accessTier string) bool {
21✔
752
        if accessTier == "" {
35✔
753
                return true
14✔
754
        }
14✔
755
        for _, tier := range storage.PossibleAccessTierValues() {
25✔
756
                if accessTier == string(tier) {
21✔
757
                        return true
3✔
758
                }
3✔
759
        }
760
        return false
4✔
761
}
762

763
// container names can contain only lowercase letters, numbers, and hyphens,
764
// and must begin and end with a letter or a number
765
func isSupportedContainerNamePrefix(prefix string) bool {
20✔
766
        if prefix == "" {
32✔
767
                return true
12✔
768
        }
12✔
769
        if len(prefix) > 20 {
9✔
770
                return false
1✔
771
        }
1✔
772
        if prefix[0] == '-' {
8✔
773
                return false
1✔
774
        }
1✔
775
        for _, v := range prefix {
19✔
776
                if v != '-' && (v < '0' || v > '9') && (v < 'a' || v > 'z') {
17✔
777
                        return false
4✔
778
                }
4✔
779
        }
780
        return true
2✔
781
}
782

783
// isNFSProtocol checks if the protocol is NFS or AZNFS
784
func isNFSProtocol(protocol string) bool {
19✔
785
        protocol = strings.ToLower(protocol)
19✔
786
        return protocol == NFS || protocol == AZNFS || protocol == NFSv3
19✔
787
}
19✔
788

789
// get storage account from secrets map
790
func getStorageAccount(secrets map[string]string) (string, string, error) {
22✔
791
        if secrets == nil {
23✔
792
                return "", "", fmt.Errorf("unexpected: getStorageAccount secrets is nil")
1✔
793
        }
1✔
794

795
        var accountName, accountKey string
21✔
796
        for k, v := range secrets {
64✔
797
                v = strings.TrimSpace(v)
43✔
798
                switch strings.ToLower(k) {
43✔
799
                case accountNameField:
7✔
800
                        accountName = v
7✔
801
                case defaultSecretAccountName: // for compatibility with built-in azurefile plugin
13✔
802
                        accountName = v
13✔
803
                case accountKeyField:
7✔
804
                        accountKey = v
7✔
805
                case defaultSecretAccountKey: // for compatibility with built-in azurefile plugin
12✔
806
                        accountKey = v
12✔
807
                }
808
        }
809

810
        if accountName == "" {
25✔
811
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountNameField, defaultSecretAccountName)
4✔
812
        }
4✔
813
        if accountKey == "" {
21✔
814
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountKeyField, defaultSecretAccountKey)
4✔
815
        }
4✔
816

817
        accountName = strings.TrimSpace(accountName)
13✔
818
        klog.V(4).Infof("got storage account(%s) from secret", accountName)
13✔
819
        return accountName, accountKey, nil
13✔
820
}
821

822
func getContainerReference(containerName string, secrets map[string]string, env az.Environment) (*azstorage.Container, error) {
9✔
823
        accountName, accountKey, rerr := getStorageAccount(secrets)
9✔
824
        if rerr != nil {
11✔
825
                return nil, rerr
2✔
826
        }
2✔
827
        client, err := azstorage.NewBasicClientOnSovereignCloud(accountName, accountKey, env)
7✔
828
        if err != nil {
13✔
829
                return nil, err
6✔
830
        }
6✔
831
        blobClient := client.GetBlobService()
1✔
832
        container := blobClient.GetContainerReference(containerName)
1✔
833
        if container == nil {
1✔
834
                return nil, fmt.Errorf("ContainerReference of %s is nil", containerName)
×
835
        }
×
836
        return container, nil
1✔
837
}
838

839
func setAzureCredentials(ctx context.Context, kubeClient kubernetes.Interface, accountName, accountKey, secretNamespace string) (string, error) {
6✔
840
        if kubeClient == nil {
8✔
841
                klog.Warningf("could not create secret: kubeClient is nil")
2✔
842
                return "", nil
2✔
843
        }
2✔
844
        if accountName == "" || accountKey == "" {
6✔
845
                return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey)
2✔
846
        }
2✔
847
        secretName := fmt.Sprintf(secretNameTemplate, accountName)
2✔
848
        secret := &v1.Secret{
2✔
849
                ObjectMeta: metav1.ObjectMeta{
2✔
850
                        Namespace: secretNamespace,
2✔
851
                        Name:      secretName,
2✔
852
                },
2✔
853
                Data: map[string][]byte{
2✔
854
                        defaultSecretAccountName: []byte(accountName),
2✔
855
                        defaultSecretAccountKey:  []byte(accountKey),
2✔
856
                },
2✔
857
                Type: "Opaque",
2✔
858
        }
2✔
859
        _, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(ctx, secret, metav1.CreateOptions{})
2✔
860
        if apierror.IsAlreadyExists(err) {
3✔
861
                err = nil
1✔
862
        }
1✔
863
        if err != nil {
2✔
864
                return "", fmt.Errorf("couldn't create secret %w", err)
×
865
        }
×
866
        return secretName, err
2✔
867
}
868

869
// GetStorageAccesskey get Azure storage account key from
870
//  1. secrets (if not empty)
871
//  2. use k8s client identity to read from k8s secret
872
//  3. use cluster identity to get from storage account directly
873
func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure.AccountOptions, secrets map[string]string, secretName, secretNamespace string) (string, string, error) {
11✔
874
        if len(secrets) > 0 {
16✔
875
                return getStorageAccount(secrets)
5✔
876
        }
5✔
877

878
        // read from k8s secret first
879
        if secretName == "" {
10✔
880
                secretName = fmt.Sprintf(secretNameTemplate, accountOptions.Name)
4✔
881
        }
4✔
882
        _, accountKey, _, _, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint
6✔
883
        if err != nil {
10✔
884
                klog.V(2).Infof("could not get account(%s) key from secret(%s) namespace(%s), error: %v, use cluster identity to get account key instead", accountOptions.Name, secretName, secretNamespace, err)
4✔
885
                accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup, accountOptions.GetLatestAccountKey)
4✔
886
        }
4✔
887
        return accountOptions.Name, accountKey, err
6✔
888
}
889

890
// GetInfoFromSecret get info from k8s secret
891
// return <accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, error>
892
func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, string, string, error) {
15✔
893
        if d.cloud.KubeClient == nil {
24✔
894
                return "", "", "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName)
9✔
895
        }
9✔
896

897
        secret, err := d.cloud.KubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
6✔
898
        if err != nil {
8✔
899
                return "", "", "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err)
2✔
900
        }
2✔
901

902
        accountName := strings.TrimSpace(string(secret.Data[defaultSecretAccountName][:]))
4✔
903
        accountKey := strings.TrimSpace(string(secret.Data[defaultSecretAccountKey][:]))
4✔
904
        accountSasToken := strings.TrimSpace(string(secret.Data[accountSasTokenField][:]))
4✔
905
        msiSecret := strings.TrimSpace(string(secret.Data[msiSecretField][:]))
4✔
906
        spnClientSecret := strings.TrimSpace(string(secret.Data[storageSPNClientSecretField][:]))
4✔
907
        spnClientID := strings.TrimSpace(string(secret.Data[storageSPNClientIDField][:]))
4✔
908
        spnTenantID := strings.TrimSpace(string(secret.Data[storageSPNTenantIDField][:]))
4✔
909

4✔
910
        klog.V(4).Infof("got storage account(%s) from secret(%s) namespace(%s)", accountName, secretName, secretNamespace)
4✔
911
        return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil
4✔
912
}
913

914
// getSubnetResourceID get default subnet resource ID from cloud provider config
915
func (d *Driver) getSubnetResourceID(vnetResourceGroup, vnetName, subnetName string) string {
6✔
916
        subsID := d.cloud.SubscriptionID
6✔
917
        if len(d.cloud.NetworkResourceSubscriptionID) > 0 {
10✔
918
                subsID = d.cloud.NetworkResourceSubscriptionID
4✔
919
        }
4✔
920

921
        if len(vnetResourceGroup) == 0 {
11✔
922
                vnetResourceGroup = d.cloud.ResourceGroup
5✔
923
                if len(d.cloud.VnetResourceGroup) > 0 {
8✔
924
                        vnetResourceGroup = d.cloud.VnetResourceGroup
3✔
925
                }
3✔
926
        }
927

928
        if len(vnetName) == 0 {
11✔
929
                vnetName = d.cloud.VnetName
5✔
930
        }
5✔
931

932
        if len(subnetName) == 0 {
11✔
933
                subnetName = d.cloud.SubnetName
5✔
934
        }
5✔
935
        return fmt.Sprintf(subnetTemplate, subsID, vnetResourceGroup, vnetName, subnetName)
6✔
936
}
937

938
func (d *Driver) useDataPlaneAPI(volumeID, accountName string) bool {
10✔
939
        cache, err := d.dataPlaneAPIVolCache.Get(volumeID, azcache.CacheReadTypeDefault)
10✔
940
        if err != nil {
10✔
941
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", volumeID, err)
×
942
        }
×
943
        if cache != nil {
13✔
944
                return true
3✔
945
        }
3✔
946
        cache, err = d.dataPlaneAPIVolCache.Get(accountName, azcache.CacheReadTypeDefault)
7✔
947
        if err != nil {
7✔
948
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", accountName, err)
×
949
        }
×
950
        if cache != nil {
7✔
951
                return true
×
952
        }
×
953
        return false
7✔
954
}
955

956
// appendDefaultMountOptions return mount options combined with mountOptions and defaultMountOptions
957
func appendDefaultMountOptions(mountOptions []string, tmpPath, containerName string) []string {
4✔
958
        var defaultMountOptions = map[string]string{
4✔
959
                "--pre-mount-validate": "true",
4✔
960
                "--use-https":          "true",
4✔
961
                "--tmp-path":           tmpPath,
4✔
962
                "--container-name":     containerName,
4✔
963
                // prevent billing charges on mounting
4✔
964
                "--cancel-list-on-mount-seconds": "10",
4✔
965
                // allow remounting using a non-empty tmp-path
4✔
966
                "--empty-dir-check": "false",
4✔
967
        }
4✔
968

4✔
969
        // stores the mount options already included in mountOptions
4✔
970
        included := make(map[string]bool)
4✔
971

4✔
972
        for _, mountOption := range mountOptions {
11✔
973
                for k := range defaultMountOptions {
49✔
974
                        if strings.HasPrefix(mountOption, k) {
46✔
975
                                included[k] = true
4✔
976
                        }
4✔
977
                }
978
        }
979

980
        allMountOptions := mountOptions
4✔
981

4✔
982
        for k, v := range defaultMountOptions {
28✔
983
                if _, isIncluded := included[k]; !isIncluded {
44✔
984
                        if v != "" {
40✔
985
                                allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", k, v))
20✔
986
                        } else {
20✔
987
                                allMountOptions = append(allMountOptions, k)
×
988
                        }
×
989
                }
990
        }
991

992
        return allMountOptions
4✔
993
}
994

995
// chmodIfPermissionMismatch only perform chmod when permission mismatches
996
func chmodIfPermissionMismatch(targetPath string, mode os.FileMode) error {
3✔
997
        info, err := os.Lstat(targetPath)
3✔
998
        if err != nil {
4✔
999
                return err
1✔
1000
        }
1✔
1001
        perm := info.Mode() & os.ModePerm
2✔
1002
        if perm != mode {
3✔
1003
                klog.V(2).Infof("chmod targetPath(%s, mode:0%o) with permissions(0%o)", targetPath, info.Mode(), mode)
1✔
1004
                if err := os.Chmod(targetPath, mode); err != nil {
1✔
1005
                        return err
×
1006
                }
×
1007
        } else {
1✔
1008
                klog.V(2).Infof("skip chmod on targetPath(%s) since mode is already 0%o)", targetPath, info.Mode())
1✔
1009
        }
1✔
1010
        return nil
2✔
1011
}
1012

1013
func createStorageAccountSecret(account, key string) map[string]string {
1✔
1014
        secret := make(map[string]string)
1✔
1015
        secret[defaultSecretAccountName] = account
1✔
1016
        secret[defaultSecretAccountKey] = key
1✔
1017
        return secret
1✔
1018
}
1✔
1019

1020
// setKeyValueInMap set key/value pair in map
1021
// key in the map is case insensitive, if key already exists, overwrite existing value
1022
func setKeyValueInMap(m map[string]string, key, value string) {
6✔
1023
        if m == nil {
7✔
1024
                return
1✔
1025
        }
1✔
1026
        for k := range m {
16✔
1027
                if strings.EqualFold(k, key) {
13✔
1028
                        m[k] = value
2✔
1029
                        return
2✔
1030
                }
2✔
1031
        }
1032
        m[key] = value
3✔
1033
}
1034

1035
// getValueInMap get value from map by key
1036
// key in the map is case insensitive
1037
func getValueInMap(m map[string]string, key string) string {
11✔
1038
        if m == nil {
12✔
1039
                return ""
1✔
1040
        }
1✔
1041
        for k, v := range m {
21✔
1042
                if strings.EqualFold(k, key) {
15✔
1043
                        return v
4✔
1044
                }
4✔
1045
        }
1046
        return ""
6✔
1047
}
1048

1049
// replaceWithMap replace key with value for str
1050
func replaceWithMap(str string, m map[string]string) string {
13✔
1051
        for k, v := range m {
18✔
1052
                if k != "" {
9✔
1053
                        str = strings.ReplaceAll(str, k, v)
4✔
1054
                }
4✔
1055
        }
1056
        return str
13✔
1057
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc