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

kubernetes-sigs / blob-csi-driver / 7412225870

04 Jan 2024 04:35PM UTC coverage: 77.871%. Remained the same
7412225870

Pull #1209

github

web-flow
chore(deps): bump golang.org/x/sync from 0.5.0 to 0.6.0

Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.5.0 to 0.6.0.
- [Commits](https://github.com/golang/sync/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1209: chore(deps): bump golang.org/x/sync from 0.5.0 to 0.6.0

2034 of 2612 relevant lines covered (77.87%)

7.29 hits per line

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

86.23
/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
        storageIdentityClientIDField   = "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
        mountOptionsField              = "mountoptions"
100
        falseValue                     = "false"
101
        trueValue                      = "true"
102
        defaultSecretAccountName       = "azurestorageaccountname"
103
        defaultSecretAccountKey        = "azurestorageaccountkey"
104
        accountSasTokenField           = "azurestorageaccountsastoken"
105
        msiSecretField                 = "msisecret"
106
        storageSPNClientSecretField    = "azurestoragespnclientsecret"
107
        Fuse                           = "fuse"
108
        Fuse2                          = "fuse2"
109
        NFS                            = "nfs"
110
        AZNFS                          = "aznfs"
111
        vnetResourceGroupField         = "vnetresourcegroup"
112
        vnetNameField                  = "vnetname"
113
        subnetNameField                = "subnetname"
114
        accessTierField                = "accesstier"
115
        networkEndpointTypeField       = "networkendpointtype"
116
        mountPermissionsField          = "mountpermissions"
117
        fsGroupChangePolicyField       = "fsgroupchangepolicy"
118
        useDataPlaneAPIField           = "usedataplaneapi"
119

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

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

132
        // 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
133
        containerMaxSize = 100 * util.TiB
134

135
        subnetTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s"
136

137
        defaultNamespace = "default"
138

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

146
        VolumeID = "volumeid"
147

148
        defaultStorageEndPointSuffix = "core.windows.net"
149

150
        FSGroupChangeNone = "None"
151
)
152

153
var (
154
        supportedProtocolList            = []string{Fuse, Fuse2, NFS}
155
        retriableErrors                  = []string{accountNotProvisioned, tooManyRequests, statusCodeNotFound, containerBeingDeletedDataplaneAPIError, containerBeingDeletedManagementAPIError, clientThrottled}
156
        supportedFSGroupChangePolicyList = []string{FSGroupChangeNone, string(v1.FSGroupChangeAlways), string(v1.FSGroupChangeOnRootMismatch)}
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
        EnableVolumeMountGroup                 bool
176
        FSGroupChangePolicy                    string
177
}
178

179
func (option *DriverOptions) AddFlags() {
1✔
180
        flag.StringVar(&option.BlobfuseProxyEndpoint, "blobfuse-proxy-endpoint", "unix://tmp/blobfuse-proxy.sock", "blobfuse-proxy endpoint")
1✔
181
        flag.StringVar(&option.NodeID, "nodeid", "", "node id")
1✔
182
        flag.StringVar(&option.DriverName, "drivername", DefaultDriverName, "name of the driver")
1✔
183
        flag.BoolVar(&option.EnableBlobfuseProxy, "enable-blobfuse-proxy", false, "using blobfuse proxy for mounts")
1✔
184
        flag.IntVar(&option.BlobfuseProxyConnTimout, "blobfuse-proxy-connect-timeout", 5, "blobfuse proxy connection timeout(seconds)")
1✔
185
        flag.BoolVar(&option.EnableBlobMockMount, "enable-blob-mock-mount", false, "enable mock mount(only for testing)")
1✔
186
        flag.BoolVar(&option.EnableGetVolumeStats, "enable-get-volume-stats", false, "allow GET_VOLUME_STATS on agent node")
1✔
187
        flag.BoolVar(&option.AppendTimeStampInCacheDir, "append-timestamp-cache-dir", false, "append timestamp into cache directory on agent node")
1✔
188
        flag.Uint64Var(&option.MountPermissions, "mount-permissions", 0777, "mounted folder permissions")
1✔
189
        flag.BoolVar(&option.AllowInlineVolumeKeyAccessWithIdentity, "allow-inline-volume-key-access-with-idenitity", false, "allow accessing storage account key using cluster identity for inline volume")
1✔
190
        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✔
191
        flag.BoolVar(&option.EnableAznfsMount, "enable-aznfs-mount", false, "replace nfs mount with aznfs mount")
1✔
192
        flag.IntVar(&option.VolStatsCacheExpireInMinutes, "vol-stats-cache-expire-in-minutes", 10, "The cache expire time in minutes for volume stats cache")
1✔
193
        flag.IntVar(&option.SasTokenExpirationMinutes, "sas-token-expiration-minutes", 1440, "sas token expiration minutes during volume cloning")
1✔
194
        flag.BoolVar(&option.EnableVolumeMountGroup, "enable-volume-mount-group", true, "indicates whether enabling VOLUME_MOUNT_GROUP")
1✔
195
        flag.StringVar(&option.FSGroupChangePolicy, "fsgroup-change-policy", "", "indicates how the volume's ownership will be changed by the driver, OnRootMismatch is the default value")
1✔
196
}
1✔
197

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

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

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

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

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

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

124✔
310
        nodeCap := []csi.NodeServiceCapability_RPC_Type{
124✔
311
                csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
124✔
312
                csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
124✔
313
        }
124✔
314
        if d.enableGetVolumeStats {
124✔
315
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
×
316
        }
×
317
        if d.enableVolumeMountGroup {
124✔
318
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_VOLUME_MOUNT_GROUP)
×
319
        }
×
320
        d.AddNodeServiceCapabilities(nodeCap)
124✔
321

124✔
322
        return &d
124✔
323
}
324

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

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

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

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

405
func checkContainerNameBeginAndEnd(containerName string) bool {
11✔
406
        length := len(containerName)
11✔
407
        if (('a' <= containerName[0] && containerName[0] <= 'z') ||
11✔
408
                ('0' <= containerName[0] && containerName[0] <= '9')) &&
11✔
409
                (('a' <= containerName[length-1] && containerName[length-1] <= 'z') ||
11✔
410
                        ('0' <= containerName[length-1] && containerName[length-1] <= '9')) {
20✔
411
                return true
9✔
412
        }
9✔
413

414
        return false
2✔
415
}
416

417
// isSASToken checks if the key contains the patterns.
418
// SAS token format could refer to https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
419
func isSASToken(key string) bool {
3✔
420
        return strings.HasPrefix(key, "?")
3✔
421
}
3✔
422

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

432
        var (
10✔
433
                subsID                  string
10✔
434
                accountKey              string
10✔
435
                accountSasToken         string
10✔
436
                msiSecret               string
10✔
437
                storageSPNClientSecret  string
10✔
438
                storageSPNClientID      string
10✔
439
                storageSPNTenantID      string
10✔
440
                secretName              string
10✔
441
                pvcNamespace            string
10✔
442
                keyVaultURL             string
10✔
443
                keyVaultSecretName      string
10✔
444
                keyVaultSecretVersion   string
10✔
445
                azureStorageAuthType    string
10✔
446
                authEnv                 []string
10✔
447
                getAccountKeyFromSecret bool
10✔
448
                getLatestAccountKey     bool
10✔
449
        )
10✔
450

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

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

507
        if secretNamespace == "" {
13✔
508
                if pvcNamespace == "" {
12✔
509
                        secretNamespace = defaultNamespace
6✔
510
                } else {
6✔
511
                        secretNamespace = pvcNamespace
×
512
                }
×
513
        }
514

515
        if rgName == "" {
8✔
516
                rgName = d.cloud.ResourceGroup
1✔
517
        }
1✔
518

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

590
        if containerName == "" {
4✔
591
                err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
592
        }
×
593

594
        if accountKey != "" {
8✔
595
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
596
        }
4✔
597

598
        if accountSasToken != "" {
5✔
599
                klog.V(2).Infof("accountSasToken is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
600
                authEnv = append(authEnv, "AZURE_STORAGE_SAS_TOKEN="+accountSasToken)
1✔
601
        }
1✔
602

603
        if msiSecret != "" {
5✔
604
                klog.V(2).Infof("msiSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
605
                authEnv = append(authEnv, "MSI_SECRET="+msiSecret)
1✔
606
        }
1✔
607

608
        if storageSPNClientSecret != "" {
5✔
609
                klog.V(2).Infof("storageSPNClientSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
610
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+storageSPNClientSecret)
1✔
611
        }
1✔
612

613
        if storageSPNClientID != "" {
4✔
614
                klog.V(2).Infof("storageSPNClientID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNClientID, accountName, containerName)
×
615
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+storageSPNClientID)
×
616
        }
×
617

618
        if storageSPNTenantID != "" {
4✔
619
                klog.V(2).Infof("storageSPNTenantID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNTenantID, accountName, containerName)
×
620
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+storageSPNTenantID)
×
621
        }
×
622

623
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
624
}
625

626
// GetStorageAccountAndContainer get storage account and container info
627
// returns <accountName, accountKey, accountSasToken, containerName>
628
// only for e2e testing
629
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
3✔
630
        var (
3✔
631
                subsID                string
3✔
632
                accountName           string
3✔
633
                accountKey            string
3✔
634
                accountSasToken       string
3✔
635
                containerName         string
3✔
636
                keyVaultURL           string
3✔
637
                keyVaultSecretName    string
3✔
638
                keyVaultSecretVersion string
3✔
639
                getLatestAccountKey   bool
3✔
640
                err                   error
3✔
641
        )
3✔
642

3✔
643
        for k, v := range attrib {
8✔
644
                switch strings.ToLower(k) {
5✔
645
                case subscriptionIDField:
×
646
                        subsID = v
×
647
                case containerNameField:
1✔
648
                        containerName = v
1✔
649
                case keyVaultURLField:
×
650
                        keyVaultURL = v
×
651
                case keyVaultSecretNameField:
1✔
652
                        keyVaultSecretName = v
1✔
653
                case keyVaultSecretVersionField:
1✔
654
                        keyVaultSecretVersion = v
1✔
655
                case storageAccountField:
×
656
                        accountName = v
×
657
                case storageAccountNameField: // for compatibility
1✔
658
                        accountName = v
1✔
659
                case getLatestAccountKeyField:
1✔
660
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
661
                                return "", "", "", "", fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
662
                        }
1✔
663
                }
664
        }
665

666
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
667
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
668
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
669
        if keyVaultURL != "" {
2✔
670
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
×
671
                if err != nil {
×
672
                        return "", "", "", "", err
×
673
                }
×
674
                if isSASToken(key) {
×
675
                        accountSasToken = key
×
676
                } else {
×
677
                        accountKey = key
×
678
                }
×
679
        } else {
2✔
680
                if len(secrets) == 0 {
4✔
681
                        var rgName string
2✔
682
                        rgName, accountName, containerName, _, _, err = GetContainerInfo(volumeID)
2✔
683
                        if err != nil {
2✔
684
                                return "", "", "", "", err
×
685
                        }
×
686

687
                        if rgName == "" {
2✔
688
                                rgName = d.cloud.ResourceGroup
×
689
                        }
×
690

691
                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
2✔
692
                        if err != nil {
3✔
693
                                return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
1✔
694
                        }
1✔
695
                }
696
        }
697

698
        if containerName == "" {
1✔
699
                return "", "", "", "", fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
700
        }
×
701

702
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
703
}
704

705
func IsCorruptedDir(dir string) bool {
4✔
706
        _, pathErr := mount.PathExists(dir)
4✔
707
        return pathErr != nil && mount.IsCorruptedMnt(pathErr)
4✔
708
}
4✔
709

710
func isRetriableError(err error) bool {
5✔
711
        if err != nil {
9✔
712
                for _, v := range retriableErrors {
19✔
713
                        if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(v)) {
18✔
714
                                return true
3✔
715
                        }
3✔
716
                }
717
        }
718
        return false
2✔
719
}
720

721
func isSupportedProtocol(protocol string) bool {
17✔
722
        if protocol == "" {
18✔
723
                return true
1✔
724
        }
1✔
725
        for _, v := range supportedProtocolList {
40✔
726
                if protocol == v {
38✔
727
                        return true
14✔
728
                }
14✔
729
        }
730
        return false
2✔
731
}
732

733
func isSupportedAccessTier(accessTier string) bool {
20✔
734
        if accessTier == "" {
33✔
735
                return true
13✔
736
        }
13✔
737
        for _, tier := range storage.PossibleAccessTierValues() {
25✔
738
                if accessTier == string(tier) {
21✔
739
                        return true
3✔
740
                }
3✔
741
        }
742
        return false
4✔
743
}
744

745
// container names can contain only lowercase letters, numbers, and hyphens,
746
// and must begin and end with a letter or a number
747
func isSupportedContainerNamePrefix(prefix string) bool {
19✔
748
        if prefix == "" {
30✔
749
                return true
11✔
750
        }
11✔
751
        if len(prefix) > 20 {
9✔
752
                return false
1✔
753
        }
1✔
754
        if prefix[0] == '-' {
8✔
755
                return false
1✔
756
        }
1✔
757
        for _, v := range prefix {
19✔
758
                if v != '-' && (v < '0' || v > '9') && (v < 'a' || v > 'z') {
17✔
759
                        return false
4✔
760
                }
4✔
761
        }
762
        return true
2✔
763
}
764

765
// isNFSProtocol checks if the protocol is NFS or AZNFS
766
func isNFSProtocol(protocol string) bool {
21✔
767
        protocol = strings.ToLower(protocol)
21✔
768
        return protocol == NFS || protocol == AZNFS
21✔
769
}
21✔
770

771
// get storage account from secrets map
772
func getStorageAccount(secrets map[string]string) (string, string, error) {
22✔
773
        if secrets == nil {
23✔
774
                return "", "", fmt.Errorf("unexpected: getStorageAccount secrets is nil")
1✔
775
        }
1✔
776

777
        var accountName, accountKey string
21✔
778
        for k, v := range secrets {
64✔
779
                v = strings.TrimSpace(v)
43✔
780
                switch strings.ToLower(k) {
43✔
781
                case accountNameField:
7✔
782
                        accountName = v
7✔
783
                case defaultSecretAccountName: // for compatibility with built-in azurefile plugin
13✔
784
                        accountName = v
13✔
785
                case accountKeyField:
7✔
786
                        accountKey = v
7✔
787
                case defaultSecretAccountKey: // for compatibility with built-in azurefile plugin
12✔
788
                        accountKey = v
12✔
789
                }
790
        }
791

792
        if accountName == "" {
25✔
793
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountNameField, defaultSecretAccountName)
4✔
794
        }
4✔
795
        if accountKey == "" {
21✔
796
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountKeyField, defaultSecretAccountKey)
4✔
797
        }
4✔
798

799
        accountName = strings.TrimSpace(accountName)
13✔
800
        klog.V(4).Infof("got storage account(%s) from secret", accountName)
13✔
801
        return accountName, accountKey, nil
13✔
802
}
803

804
func getContainerReference(containerName string, secrets map[string]string, env az.Environment) (*azstorage.Container, error) {
9✔
805
        accountName, accountKey, rerr := getStorageAccount(secrets)
9✔
806
        if rerr != nil {
11✔
807
                return nil, rerr
2✔
808
        }
2✔
809
        client, err := azstorage.NewBasicClientOnSovereignCloud(accountName, accountKey, env)
7✔
810
        if err != nil {
13✔
811
                return nil, err
6✔
812
        }
6✔
813
        blobClient := client.GetBlobService()
1✔
814
        container := blobClient.GetContainerReference(containerName)
1✔
815
        if container == nil {
1✔
816
                return nil, fmt.Errorf("ContainerReference of %s is nil", containerName)
×
817
        }
×
818
        return container, nil
1✔
819
}
820

821
func setAzureCredentials(ctx context.Context, kubeClient kubernetes.Interface, accountName, accountKey, secretNamespace string) (string, error) {
6✔
822
        if kubeClient == nil {
8✔
823
                klog.Warningf("could not create secret: kubeClient is nil")
2✔
824
                return "", nil
2✔
825
        }
2✔
826
        if accountName == "" || accountKey == "" {
6✔
827
                return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey)
2✔
828
        }
2✔
829
        secretName := fmt.Sprintf(secretNameTemplate, accountName)
2✔
830
        secret := &v1.Secret{
2✔
831
                ObjectMeta: metav1.ObjectMeta{
2✔
832
                        Namespace: secretNamespace,
2✔
833
                        Name:      secretName,
2✔
834
                },
2✔
835
                Data: map[string][]byte{
2✔
836
                        defaultSecretAccountName: []byte(accountName),
2✔
837
                        defaultSecretAccountKey:  []byte(accountKey),
2✔
838
                },
2✔
839
                Type: "Opaque",
2✔
840
        }
2✔
841
        _, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(ctx, secret, metav1.CreateOptions{})
2✔
842
        if apierror.IsAlreadyExists(err) {
3✔
843
                err = nil
1✔
844
        }
1✔
845
        if err != nil {
2✔
846
                return "", fmt.Errorf("couldn't create secret %w", err)
×
847
        }
×
848
        return secretName, err
2✔
849
}
850

851
// GetStorageAccesskey get Azure storage account key from
852
//  1. secrets (if not empty)
853
//  2. use k8s client identity to read from k8s secret
854
//  3. use cluster identity to get from storage account directly
855
func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure.AccountOptions, secrets map[string]string, secretName, secretNamespace string) (string, string, error) {
11✔
856
        if len(secrets) > 0 {
16✔
857
                return getStorageAccount(secrets)
5✔
858
        }
5✔
859

860
        // read from k8s secret first
861
        if secretName == "" {
10✔
862
                secretName = fmt.Sprintf(secretNameTemplate, accountOptions.Name)
4✔
863
        }
4✔
864
        _, accountKey, _, _, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint
6✔
865
        if err != nil {
10✔
866
                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✔
867
                accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup, accountOptions.GetLatestAccountKey)
4✔
868
        }
4✔
869
        return accountOptions.Name, accountKey, err
6✔
870
}
871

872
// GetInfoFromSecret get info from k8s secret
873
// return <accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, error>
874
func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, string, string, error) {
15✔
875
        if d.cloud.KubeClient == nil {
24✔
876
                return "", "", "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName)
9✔
877
        }
9✔
878

879
        secret, err := d.cloud.KubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
6✔
880
        if err != nil {
8✔
881
                return "", "", "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err)
2✔
882
        }
2✔
883

884
        accountName := strings.TrimSpace(string(secret.Data[defaultSecretAccountName][:]))
4✔
885
        accountKey := strings.TrimSpace(string(secret.Data[defaultSecretAccountKey][:]))
4✔
886
        accountSasToken := strings.TrimSpace(string(secret.Data[accountSasTokenField][:]))
4✔
887
        msiSecret := strings.TrimSpace(string(secret.Data[msiSecretField][:]))
4✔
888
        spnClientSecret := strings.TrimSpace(string(secret.Data[storageSPNClientSecretField][:]))
4✔
889
        spnClientID := strings.TrimSpace(string(secret.Data[storageSPNClientIDField][:]))
4✔
890
        spnTenantID := strings.TrimSpace(string(secret.Data[storageSPNTenantIDField][:]))
4✔
891

4✔
892
        klog.V(4).Infof("got storage account(%s) from secret(%s) namespace(%s)", accountName, secretName, secretNamespace)
4✔
893
        return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil
4✔
894
}
895

896
// getSubnetResourceID get default subnet resource ID from cloud provider config
897
func (d *Driver) getSubnetResourceID(vnetResourceGroup, vnetName, subnetName string) string {
6✔
898
        subsID := d.cloud.SubscriptionID
6✔
899
        if len(d.cloud.NetworkResourceSubscriptionID) > 0 {
10✔
900
                subsID = d.cloud.NetworkResourceSubscriptionID
4✔
901
        }
4✔
902

903
        if len(vnetResourceGroup) == 0 {
11✔
904
                vnetResourceGroup = d.cloud.ResourceGroup
5✔
905
                if len(d.cloud.VnetResourceGroup) > 0 {
8✔
906
                        vnetResourceGroup = d.cloud.VnetResourceGroup
3✔
907
                }
3✔
908
        }
909

910
        if len(vnetName) == 0 {
11✔
911
                vnetName = d.cloud.VnetName
5✔
912
        }
5✔
913

914
        if len(subnetName) == 0 {
11✔
915
                subnetName = d.cloud.SubnetName
5✔
916
        }
5✔
917
        return fmt.Sprintf(subnetTemplate, subsID, vnetResourceGroup, vnetName, subnetName)
6✔
918
}
919

920
func (d *Driver) useDataPlaneAPI(volumeID, accountName string) bool {
4✔
921
        cache, err := d.dataPlaneAPIVolCache.Get(volumeID, azcache.CacheReadTypeDefault)
4✔
922
        if err != nil {
4✔
923
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", volumeID, err)
×
924
        }
×
925
        if cache != nil {
7✔
926
                return true
3✔
927
        }
3✔
928
        cache, err = d.dataPlaneAPIVolCache.Get(accountName, azcache.CacheReadTypeDefault)
1✔
929
        if err != nil {
1✔
930
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", accountName, err)
×
931
        }
×
932
        if cache != nil {
1✔
933
                return true
×
934
        }
×
935
        return false
1✔
936
}
937

938
// appendDefaultMountOptions return mount options combined with mountOptions and defaultMountOptions
939
func appendDefaultMountOptions(mountOptions []string, tmpPath, containerName string) []string {
4✔
940
        var defaultMountOptions = map[string]string{
4✔
941
                "--pre-mount-validate": "true",
4✔
942
                "--use-https":          "true",
4✔
943
                "--tmp-path":           tmpPath,
4✔
944
                "--container-name":     containerName,
4✔
945
                // prevent billing charges on mounting
4✔
946
                "--cancel-list-on-mount-seconds": "10",
4✔
947
                // allow remounting using a non-empty tmp-path
4✔
948
                "--empty-dir-check": "false",
4✔
949
        }
4✔
950

4✔
951
        // stores the mount options already included in mountOptions
4✔
952
        included := make(map[string]bool)
4✔
953

4✔
954
        for _, mountOption := range mountOptions {
11✔
955
                for k := range defaultMountOptions {
49✔
956
                        if strings.HasPrefix(mountOption, k) {
46✔
957
                                included[k] = true
4✔
958
                        }
4✔
959
                }
960
        }
961

962
        allMountOptions := mountOptions
4✔
963

4✔
964
        for k, v := range defaultMountOptions {
28✔
965
                if _, isIncluded := included[k]; !isIncluded {
44✔
966
                        if v != "" {
40✔
967
                                allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", k, v))
20✔
968
                        } else {
20✔
969
                                allMountOptions = append(allMountOptions, k)
×
970
                        }
×
971
                }
972
        }
973

974
        return allMountOptions
4✔
975
}
976

977
// chmodIfPermissionMismatch only perform chmod when permission mismatches
978
func chmodIfPermissionMismatch(targetPath string, mode os.FileMode) error {
3✔
979
        info, err := os.Lstat(targetPath)
3✔
980
        if err != nil {
4✔
981
                return err
1✔
982
        }
1✔
983
        perm := info.Mode() & os.ModePerm
2✔
984
        if perm != mode {
3✔
985
                klog.V(2).Infof("chmod targetPath(%s, mode:0%o) with permissions(0%o)", targetPath, info.Mode(), mode)
1✔
986
                if err := os.Chmod(targetPath, mode); err != nil {
1✔
987
                        return err
×
988
                }
×
989
        } else {
1✔
990
                klog.V(2).Infof("skip chmod on targetPath(%s) since mode is already 0%o)", targetPath, info.Mode())
1✔
991
        }
1✔
992
        return nil
2✔
993
}
994

995
func createStorageAccountSecret(account, key string) map[string]string {
1✔
996
        secret := make(map[string]string)
1✔
997
        secret[defaultSecretAccountName] = account
1✔
998
        secret[defaultSecretAccountKey] = key
1✔
999
        return secret
1✔
1000
}
1✔
1001

1002
// setKeyValueInMap set key/value pair in map
1003
// key in the map is case insensitive, if key already exists, overwrite existing value
1004
func setKeyValueInMap(m map[string]string, key, value string) {
6✔
1005
        if m == nil {
7✔
1006
                return
1✔
1007
        }
1✔
1008
        for k := range m {
16✔
1009
                if strings.EqualFold(k, key) {
13✔
1010
                        m[k] = value
2✔
1011
                        return
2✔
1012
                }
2✔
1013
        }
1014
        m[key] = value
3✔
1015
}
1016

1017
// getValueInMap get value from map by key
1018
// key in the map is case insensitive
1019
func getValueInMap(m map[string]string, key string) string {
7✔
1020
        if m == nil {
8✔
1021
                return ""
1✔
1022
        }
1✔
1023
        for k, v := range m {
11✔
1024
                if strings.EqualFold(k, key) {
9✔
1025
                        return v
4✔
1026
                }
4✔
1027
        }
1028
        return ""
2✔
1029
}
1030

1031
// replaceWithMap replace key with value for str
1032
func replaceWithMap(str string, m map[string]string) string {
13✔
1033
        for k, v := range m {
18✔
1034
                if k != "" {
9✔
1035
                        str = strings.ReplaceAll(str, k, v)
4✔
1036
                }
4✔
1037
        }
1038
        return str
13✔
1039
}
1040

1041
func isSupportedFSGroupChangePolicy(policy string) bool {
27✔
1042
        if policy == "" {
47✔
1043
                return true
20✔
1044
        }
20✔
1045
        for _, v := range supportedFSGroupChangePolicyList {
25✔
1046
                if policy == v {
21✔
1047
                        return true
3✔
1048
                }
3✔
1049
        }
1050
        return false
4✔
1051
}
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