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

kubernetes-sigs / blob-csi-driver / 10104281708

26 Jul 2024 01:43AM UTC coverage: 74.206%. Remained the same
10104281708

push

github

web-flow
Update csi-debug.md

2221 of 2993 relevant lines covered (74.21%)

7.19 hits per line

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

84.85
/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/sdk/resourcemanager/storage/armstorage"
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
        "sigs.k8s.io/cloud-provider-azure/pkg/azclient"
48
        azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
49
        "sigs.k8s.io/cloud-provider-azure/pkg/provider"
50
        azure "sigs.k8s.io/cloud-provider-azure/pkg/provider"
51
)
52

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

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

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

138
        // 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
139
        containerMaxSize = 100 * util.TiB
140

141
        subnetTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s"
142

143
        defaultNamespace = "default"
144

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

152
        VolumeID = "volumeid"
153

154
        defaultStorageEndPointSuffix = "core.windows.net"
155

156
        FSGroupChangeNone = "None"
157
        // define tag value delimiter and default is comma
158
        tagValueDelimiterField = "tagValueDelimiter"
159
)
160

161
var (
162
        supportedProtocolList            = []string{Fuse, Fuse2, NFS, AZNFS}
163
        retriableErrors                  = []string{accountNotProvisioned, tooManyRequests, statusCodeNotFound, containerBeingDeletedDataplaneAPIError, containerBeingDeletedManagementAPIError, clientThrottled}
164
        supportedFSGroupChangePolicyList = []string{FSGroupChangeNone, string(v1.FSGroupChangeAlways), string(v1.FSGroupChangeOnRootMismatch)}
165
)
166

167
// DriverOptions defines driver parameters specified in driver deployment
168
type DriverOptions struct {
169
        NodeID                                 string
170
        DriverName                             string
171
        BlobfuseProxyEndpoint                  string
172
        EnableBlobfuseProxy                    bool
173
        BlobfuseProxyConnTimout                int
174
        EnableBlobMockMount                    bool
175
        AllowInlineVolumeKeyAccessWithIdentity bool
176
        EnableGetVolumeStats                   bool
177
        AppendTimeStampInCacheDir              bool
178
        AppendMountErrorHelpLink               bool
179
        MountPermissions                       uint64
180
        EnableAznfsMount                       bool
181
        VolStatsCacheExpireInMinutes           int
182
        SasTokenExpirationMinutes              int
183
        WaitForAzCopyTimeoutMinutes            int
184
        EnableVolumeMountGroup                 bool
185
        FSGroupChangePolicy                    string
186
}
187

188
func (option *DriverOptions) AddFlags() {
1✔
189
        flag.StringVar(&option.BlobfuseProxyEndpoint, "blobfuse-proxy-endpoint", "unix://tmp/blobfuse-proxy.sock", "blobfuse-proxy endpoint")
1✔
190
        flag.StringVar(&option.NodeID, "nodeid", "", "node id")
1✔
191
        flag.StringVar(&option.DriverName, "drivername", DefaultDriverName, "name of the driver")
1✔
192
        flag.BoolVar(&option.EnableBlobfuseProxy, "enable-blobfuse-proxy", false, "using blobfuse proxy for mounts")
1✔
193
        flag.IntVar(&option.BlobfuseProxyConnTimout, "blobfuse-proxy-connect-timeout", 5, "blobfuse proxy connection timeout(seconds)")
1✔
194
        flag.BoolVar(&option.EnableBlobMockMount, "enable-blob-mock-mount", false, "enable mock mount(only for testing)")
1✔
195
        flag.BoolVar(&option.EnableGetVolumeStats, "enable-get-volume-stats", false, "allow GET_VOLUME_STATS on agent node")
1✔
196
        flag.BoolVar(&option.AppendTimeStampInCacheDir, "append-timestamp-cache-dir", false, "append timestamp into cache directory on agent node")
1✔
197
        flag.Uint64Var(&option.MountPermissions, "mount-permissions", 0777, "mounted folder permissions")
1✔
198
        flag.BoolVar(&option.AllowInlineVolumeKeyAccessWithIdentity, "allow-inline-volume-key-access-with-idenitity", false, "allow accessing storage account key using cluster identity for inline volume")
1✔
199
        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✔
200
        flag.BoolVar(&option.EnableAznfsMount, "enable-aznfs-mount", false, "replace nfs mount with aznfs mount")
1✔
201
        flag.IntVar(&option.VolStatsCacheExpireInMinutes, "vol-stats-cache-expire-in-minutes", 10, "The cache expire time in minutes for volume stats cache")
1✔
202
        flag.IntVar(&option.SasTokenExpirationMinutes, "sas-token-expiration-minutes", 1440, "sas token expiration minutes during volume cloning")
1✔
203
        flag.IntVar(&option.WaitForAzCopyTimeoutMinutes, "wait-for-azcopy-timeout-minutes", 18, "timeout in minutes for waiting for azcopy to finish")
1✔
204
        flag.BoolVar(&option.EnableVolumeMountGroup, "enable-volume-mount-group", true, "indicates whether enabling VOLUME_MOUNT_GROUP")
1✔
205
        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✔
206
}
1✔
207

208
// Driver implements all interfaces of CSI drivers
209
type Driver struct {
210
        csicommon.CSIDriver
211

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

254
// NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version &
255
// does not support optional driver plugin info manifest field. Refer to CSI spec for more details.
256
func NewDriver(options *DriverOptions, kubeClient kubernetes.Interface, cloud *provider.Cloud) *Driver {
135✔
257
        d := Driver{
135✔
258
                volLockMap:                             util.NewLockMap(),
135✔
259
                subnetLockMap:                          util.NewLockMap(),
135✔
260
                volumeLocks:                            newVolumeLocks(),
135✔
261
                blobfuseProxyEndpoint:                  options.BlobfuseProxyEndpoint,
135✔
262
                enableBlobfuseProxy:                    options.EnableBlobfuseProxy,
135✔
263
                allowInlineVolumeKeyAccessWithIdentity: options.AllowInlineVolumeKeyAccessWithIdentity,
135✔
264
                blobfuseProxyConnTimout:                options.BlobfuseProxyConnTimout,
135✔
265
                enableBlobMockMount:                    options.EnableBlobMockMount,
135✔
266
                enableGetVolumeStats:                   options.EnableGetVolumeStats,
135✔
267
                enableVolumeMountGroup:                 options.EnableVolumeMountGroup,
135✔
268
                appendMountErrorHelpLink:               options.AppendMountErrorHelpLink,
135✔
269
                mountPermissions:                       options.MountPermissions,
135✔
270
                enableAznfsMount:                       options.EnableAznfsMount,
135✔
271
                sasTokenExpirationMinutes:              options.SasTokenExpirationMinutes,
135✔
272
                waitForAzCopyTimeoutMinutes:            options.WaitForAzCopyTimeoutMinutes,
135✔
273
                fsGroupChangePolicy:                    options.FSGroupChangePolicy,
135✔
274
                azcopy:                                 &util.Azcopy{},
135✔
275
                KubeClient:                             kubeClient,
135✔
276
                cloud:                                  cloud,
135✔
277
        }
135✔
278
        d.Name = options.DriverName
135✔
279
        d.Version = driverVersion
135✔
280
        d.NodeID = options.NodeID
135✔
281
        if d.cloud != nil {
270✔
282
                d.clientFactory = d.cloud.ComputeClientFactory
135✔
283
                d.networkClientFactory = d.cloud.NetworkClientFactory
135✔
284
        }
135✔
285

286
        var err error
135✔
287
        getter := func(key string) (interface{}, error) { return nil, nil }
159✔
288
        if d.accountSearchCache, err = azcache.NewTimedCache(time.Minute, getter, false); err != nil {
135✔
289
                klog.Fatalf("%v", err)
×
290
        }
×
291
        if d.dataPlaneAPIVolCache, err = azcache.NewTimedCache(24*30*time.Hour, getter, false); err != nil {
135✔
292
                klog.Fatalf("%v", err)
×
293
        }
×
294
        if d.azcopySasTokenCache, err = azcache.NewTimedCache(15*time.Minute, getter, false); err != nil {
135✔
295
                klog.Fatalf("%v", err)
×
296
        }
×
297

298
        if options.VolStatsCacheExpireInMinutes <= 0 {
270✔
299
                options.VolStatsCacheExpireInMinutes = 10 // default expire in 10 minutes
135✔
300
        }
135✔
301
        if d.volStatsCache, err = azcache.NewTimedCache(time.Duration(options.VolStatsCacheExpireInMinutes)*time.Minute, getter, false); err != nil {
135✔
302
                klog.Fatalf("%v", err)
×
303
        }
×
304
        d.mounter = &mount.SafeFormatAndMount{
135✔
305
                Interface: mount.New(""),
135✔
306
                Exec:      utilexec.New(),
135✔
307
        }
135✔
308

135✔
309
        // Initialize default library driver
135✔
310
        d.AddControllerServiceCapabilities(
135✔
311
                []csi.ControllerServiceCapability_RPC_Type{
135✔
312
                        csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
135✔
313
                        //csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
135✔
314
                        //csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
135✔
315
                        csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
135✔
316
                        csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
135✔
317
                        csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
135✔
318
                })
135✔
319
        d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{
135✔
320
                csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
135✔
321
                csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
135✔
322
                csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
135✔
323
                csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
135✔
324
                csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
135✔
325
                csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER,
135✔
326
                csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
135✔
327
        })
135✔
328

135✔
329
        nodeCap := []csi.NodeServiceCapability_RPC_Type{
135✔
330
                csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
135✔
331
                csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
135✔
332
        }
135✔
333
        if d.enableGetVolumeStats {
135✔
334
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
×
335
        }
×
336
        if d.enableVolumeMountGroup {
135✔
337
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_VOLUME_MOUNT_GROUP)
×
338
        }
×
339
        d.AddNodeServiceCapabilities(nodeCap)
135✔
340

135✔
341
        return &d
135✔
342
}
343

344
// Run driver initialization
345
func (d *Driver) Run(ctx context.Context, endpoint string) error {
2✔
346
        versionMeta, err := GetVersionYAML(d.Name)
2✔
347
        if err != nil {
2✔
348
                klog.Fatalf("%v", err)
×
349
        }
×
350
        klog.Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta)
2✔
351
        grpcInterceptor := grpc.UnaryInterceptor(csicommon.LogGRPC)
2✔
352
        opts := []grpc.ServerOption{
2✔
353
                grpcInterceptor,
2✔
354
        }
2✔
355
        s := grpc.NewServer(opts...)
2✔
356
        csi.RegisterIdentityServer(s, d)
2✔
357
        csi.RegisterControllerServer(s, d)
2✔
358
        csi.RegisterNodeServer(s, d)
2✔
359

2✔
360
        go func() {
4✔
361
                //graceful shutdown
2✔
362
                <-ctx.Done()
2✔
363
                s.GracefulStop()
2✔
364
        }()
2✔
365
        // Driver d act as IdentityServer, ControllerServer and NodeServer
366
        listener, err := csicommon.Listen(ctx, endpoint)
2✔
367
        if err != nil {
2✔
368
                klog.Fatalf("failed to listen to endpoint, error: %v", err)
×
369
        }
×
370
        err = s.Serve(listener)
2✔
371
        if errors.Is(err, grpc.ErrServerStopped) {
2✔
372
                klog.Infof("gRPC server stopped serving")
×
373
                return nil
×
374
        }
×
375
        return err
2✔
376
}
377

378
// GetContainerInfo get container info according to volume id
379
// the format of VolumeId is: rg#accountName#containerName#uuid#secretNamespace#subsID
380
//
381
// e.g.
382
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#"
383
// output: rg, f5713de20cde511e8ba4900, containerName, "" , ""
384
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#"
385
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, ""
386
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#subsID"
387
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, subsID
388
func GetContainerInfo(id string) (string, string, string, string, string, error) {
39✔
389
        segments := strings.Split(id, separator)
39✔
390
        if len(segments) < 3 {
48✔
391
                return "", "", "", "", "", fmt.Errorf("error parsing volume id: %q, should at least contain two #", id)
9✔
392
        }
9✔
393
        var secretNamespace, subsID string
30✔
394
        if len(segments) > 4 {
36✔
395
                secretNamespace = segments[4]
6✔
396
        }
6✔
397
        if len(segments) > 5 {
33✔
398
                subsID = segments[5]
3✔
399
        }
3✔
400
        return segments[0], segments[1], segments[2], secretNamespace, subsID, nil
30✔
401
}
402

403
// A container name must be a valid DNS name, conforming to the following naming rules:
404
//  1. Container names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
405
//  2. Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names.
406
//  3. All letters in a container name must be lowercase.
407
//  4. Container names must be from 3 through 63 characters long.
408
//
409
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
410
func getValidContainerName(volumeName, protocol string) string {
5✔
411
        containerName := strings.ToLower(volumeName)
5✔
412
        if len(containerName) > containerNameMaxLength {
6✔
413
                containerName = containerName[0:containerNameMaxLength]
1✔
414
        }
1✔
415
        if !checkContainerNameBeginAndEnd(containerName) || len(containerName) < containerNameMinLength {
5✔
416
                // now we set as 63 for maximum container name length
×
417
                // todo: get cluster name
×
418
                containerName = k8sutil.GenerateVolumeName(fmt.Sprintf("pvc-%s", protocol), uuid.NewUUID().String(), 63)
×
419
                klog.Warningf("requested volume name (%s) is invalid, regenerated as (%q)", volumeName, containerName)
×
420
        }
×
421
        return strings.Replace(containerName, "--", "-", -1)
5✔
422
}
423

424
func checkContainerNameBeginAndEnd(containerName string) bool {
11✔
425
        length := len(containerName)
11✔
426
        if (('a' <= containerName[0] && containerName[0] <= 'z') ||
11✔
427
                ('0' <= containerName[0] && containerName[0] <= '9')) &&
11✔
428
                (('a' <= containerName[length-1] && containerName[length-1] <= 'z') ||
11✔
429
                        ('0' <= containerName[length-1] && containerName[length-1] <= '9')) {
20✔
430
                return true
9✔
431
        }
9✔
432

433
        return false
2✔
434
}
435

436
// isSASToken checks if the key contains the patterns.
437
// SAS token format could refer to https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
438
func isSASToken(key string) bool {
3✔
439
        return strings.HasPrefix(key, "?")
3✔
440
}
3✔
441

442
// GetAuthEnv return <accountName, containerName, authEnv, error>
443
func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attrib, secrets map[string]string) (string, string, string, string, []string, error) {
10✔
444
        rgName, accountName, containerName, secretNamespace, _, err := GetContainerInfo(volumeID)
10✔
445
        if err != nil {
12✔
446
                // ignore volumeID parsing error
2✔
447
                klog.V(2).Infof("parsing volumeID(%s) return with error: %v", volumeID, err)
2✔
448
                err = nil
2✔
449
        }
2✔
450

451
        var (
10✔
452
                subsID                  string
10✔
453
                accountKey              string
10✔
454
                accountSasToken         string
10✔
455
                msiSecret               string
10✔
456
                storageSPNClientSecret  string
10✔
457
                storageSPNClientID      string
10✔
458
                storageSPNTenantID      string
10✔
459
                secretName              string
10✔
460
                pvcNamespace            string
10✔
461
                keyVaultURL             string
10✔
462
                keyVaultSecretName      string
10✔
463
                keyVaultSecretVersion   string
10✔
464
                azureStorageAuthType    string
10✔
465
                authEnv                 []string
10✔
466
                getAccountKeyFromSecret bool
10✔
467
                getLatestAccountKey     bool
10✔
468
                clientID                string
10✔
469
                tenantID                string
10✔
470
                serviceAccountToken     string
10✔
471
        )
10✔
472

10✔
473
        for k, v := range attrib {
36✔
474
                switch strings.ToLower(k) {
26✔
475
                case subscriptionIDField:
1✔
476
                        subsID = v
1✔
477
                case resourceGroupField:
×
478
                        rgName = v
×
479
                case containerNameField:
3✔
480
                        containerName = v
3✔
481
                case keyVaultURLField:
1✔
482
                        keyVaultURL = v
1✔
483
                case keyVaultSecretNameField:
1✔
484
                        keyVaultSecretName = v
1✔
485
                case keyVaultSecretVersionField:
1✔
486
                        keyVaultSecretVersion = v
1✔
487
                case storageAccountField:
2✔
488
                        accountName = v
2✔
489
                case storageAccountNameField: // for compatibility
1✔
490
                        accountName = v
1✔
491
                case secretNameField:
1✔
492
                        secretName = v
1✔
493
                case secretNamespaceField:
1✔
494
                        secretNamespace = v
1✔
495
                case pvcNamespaceKey:
1✔
496
                        pvcNamespace = v
1✔
497
                case getAccountKeyFromSecretField:
1✔
498
                        getAccountKeyFromSecret = strings.EqualFold(v, trueValue)
1✔
499
                case storageAuthTypeField:
×
500
                        azureStorageAuthType = v
×
501
                        authEnv = append(authEnv, "AZURE_STORAGE_AUTH_TYPE="+v)
×
502
                case storageIdentityClientIDField:
1✔
503
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_CLIENT_ID="+v)
1✔
504
                case storageIdentityObjectIDField:
1✔
505
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_OBJECT_ID="+v)
1✔
506
                case storageIdentityResourceIDField:
1✔
507
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_RESOURCE_ID="+v)
1✔
508
                case msiEndpointField:
1✔
509
                        authEnv = append(authEnv, "MSI_ENDPOINT="+v)
1✔
510
                case storageSPNClientIDField:
1✔
511
                        storageSPNClientID = v
1✔
512
                case storageSPNTenantIDField:
1✔
513
                        storageSPNTenantID = v
1✔
514
                case storageAADEndpointField:
1✔
515
                        authEnv = append(authEnv, "AZURE_STORAGE_AAD_ENDPOINT="+v)
1✔
516
                case getLatestAccountKeyField:
1✔
517
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
518
                                return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
519
                        }
1✔
520
                case strings.ToLower(clientIDField):
×
521
                        clientID = v
×
522
                case strings.ToLower(tenantIDField):
×
523
                        tenantID = v
×
524
                case strings.ToLower(serviceAccountTokenField):
×
525
                        serviceAccountToken = v
×
526
                }
527
        }
528
        klog.V(2).Infof("volumeID(%s) authEnv: %s", volumeID, authEnv)
9✔
529

9✔
530
        if protocol == NFS {
11✔
531
                // nfs protocol does not need account key, return directly
2✔
532
                return rgName, accountName, accountKey, containerName, authEnv, err
2✔
533
        }
2✔
534

535
        if secretNamespace == "" {
13✔
536
                if pvcNamespace == "" {
12✔
537
                        secretNamespace = defaultNamespace
6✔
538
                } else {
6✔
539
                        secretNamespace = pvcNamespace
×
540
                }
×
541
        }
542

543
        if rgName == "" {
8✔
544
                rgName = d.cloud.ResourceGroup
1✔
545
        }
1✔
546

547
        if tenantID == "" {
14✔
548
                tenantID = d.cloud.TenantID
7✔
549
        }
7✔
550

551
        // if client id is specified, we only use service account token to get account key
552
        if clientID != "" {
7✔
553
                klog.V(2).Infof("clientID(%s) is specified, use service account token to get account key", clientID)
×
554
                if subsID == "" {
×
555
                        subsID = d.cloud.SubscriptionID
×
556
                }
×
557
                accountKey, err := d.cloud.GetStorageAccesskeyFromServiceAccountToken(ctx, subsID, accountName, rgName, clientID, tenantID, serviceAccountToken)
×
558
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
×
559
                return rgName, accountName, accountKey, containerName, authEnv, err
×
560
        }
561

562
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
563
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
564
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
565
        if keyVaultURL != "" {
8✔
566
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
1✔
567
                if err != nil {
2✔
568
                        return rgName, accountName, accountKey, containerName, authEnv, err
1✔
569
                }
1✔
570
                if isSASToken(key) {
×
571
                        accountSasToken = key
×
572
                } else {
×
573
                        accountKey = key
×
574
                }
×
575
        } else {
6✔
576
                if len(secrets) == 0 {
11✔
577
                        if secretName == "" && accountName != "" {
9✔
578
                                secretName = fmt.Sprintf(secretNameTemplate, accountName)
4✔
579
                        }
4✔
580
                        if secretName != "" {
10✔
581
                                // read from k8s secret first
5✔
582
                                var name, spnClientID, spnTenantID string
5✔
583
                                name, accountKey, accountSasToken, msiSecret, storageSPNClientSecret, spnClientID, spnTenantID, err = d.GetInfoFromSecret(ctx, secretName, secretNamespace)
5✔
584
                                if name != "" {
5✔
585
                                        accountName = name
×
586
                                }
×
587
                                if spnClientID != "" {
5✔
588
                                        storageSPNClientID = spnClientID
×
589
                                }
×
590
                                if spnTenantID != "" {
5✔
591
                                        storageSPNTenantID = spnTenantID
×
592
                                }
×
593
                                if err != nil && strings.EqualFold(azureStorageAuthType, "msi") {
5✔
594
                                        klog.V(2).Infof("ignore error(%v) since secret is optional for auth type(%s)", err, azureStorageAuthType)
×
595
                                        err = nil
×
596
                                }
×
597
                                if err != nil && !getAccountKeyFromSecret && (azureStorageAuthType == "" || strings.EqualFold(azureStorageAuthType, "key")) {
10✔
598
                                        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✔
599
                                                accountName, secretNamespace, secretName, err)
5✔
600
                                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
5✔
601
                                        if err != nil {
7✔
602
                                                return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
2✔
603
                                        }
2✔
604
                                }
605
                        }
606
                } else {
1✔
607
                        for k, v := range secrets {
8✔
608
                                v = strings.TrimSpace(v)
7✔
609
                                switch strings.ToLower(k) {
7✔
610
                                case accountNameField:
1✔
611
                                        accountName = v
1✔
612
                                case defaultSecretAccountName: // for compatibility with built-in blobfuse plugin
1✔
613
                                        accountName = v
1✔
614
                                case accountKeyField:
1✔
615
                                        accountKey = v
1✔
616
                                case defaultSecretAccountKey: // for compatibility with built-in blobfuse plugin
1✔
617
                                        accountKey = v
1✔
618
                                case accountSasTokenField:
1✔
619
                                        accountSasToken = v
1✔
620
                                case msiSecretField:
1✔
621
                                        msiSecret = v
1✔
622
                                case storageSPNClientSecretField:
1✔
623
                                        storageSPNClientSecret = v
1✔
624
                                case storageSPNClientIDField:
×
625
                                        storageSPNClientID = v
×
626
                                case storageSPNTenantIDField:
×
627
                                        storageSPNTenantID = v
×
628
                                }
629
                        }
630
                }
631
        }
632

633
        if containerName == "" {
4✔
634
                err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
635
        }
×
636

637
        if accountKey != "" {
8✔
638
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
639
        }
4✔
640

641
        if accountSasToken != "" {
5✔
642
                klog.V(2).Infof("accountSasToken is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
643
                authEnv = append(authEnv, "AZURE_STORAGE_SAS_TOKEN="+accountSasToken)
1✔
644
        }
1✔
645

646
        if msiSecret != "" {
5✔
647
                klog.V(2).Infof("msiSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
648
                authEnv = append(authEnv, "MSI_SECRET="+msiSecret)
1✔
649
        }
1✔
650

651
        if storageSPNClientSecret != "" {
5✔
652
                klog.V(2).Infof("storageSPNClientSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
653
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+storageSPNClientSecret)
1✔
654
        }
1✔
655

656
        if storageSPNClientID != "" {
4✔
657
                klog.V(2).Infof("storageSPNClientID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNClientID, accountName, containerName)
×
658
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+storageSPNClientID)
×
659
        }
×
660

661
        if storageSPNTenantID != "" {
4✔
662
                klog.V(2).Infof("storageSPNTenantID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNTenantID, accountName, containerName)
×
663
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+storageSPNTenantID)
×
664
        }
×
665

666
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
667
}
668

669
// GetStorageAccountAndContainer get storage account and container info
670
// returns <accountName, accountKey, accountSasToken, containerName>
671
// only for e2e testing
672
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
3✔
673
        var (
3✔
674
                subsID                string
3✔
675
                accountName           string
3✔
676
                accountKey            string
3✔
677
                accountSasToken       string
3✔
678
                containerName         string
3✔
679
                keyVaultURL           string
3✔
680
                keyVaultSecretName    string
3✔
681
                keyVaultSecretVersion string
3✔
682
                getLatestAccountKey   bool
3✔
683
                err                   error
3✔
684
        )
3✔
685

3✔
686
        for k, v := range attrib {
8✔
687
                switch strings.ToLower(k) {
5✔
688
                case subscriptionIDField:
×
689
                        subsID = v
×
690
                case containerNameField:
1✔
691
                        containerName = v
1✔
692
                case keyVaultURLField:
×
693
                        keyVaultURL = v
×
694
                case keyVaultSecretNameField:
1✔
695
                        keyVaultSecretName = v
1✔
696
                case keyVaultSecretVersionField:
1✔
697
                        keyVaultSecretVersion = v
1✔
698
                case storageAccountField:
×
699
                        accountName = v
×
700
                case storageAccountNameField: // for compatibility
1✔
701
                        accountName = v
1✔
702
                case getLatestAccountKeyField:
1✔
703
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
704
                                return "", "", "", "", fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
705
                        }
1✔
706
                }
707
        }
708

709
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
710
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
711
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
712
        if keyVaultURL != "" {
2✔
713
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
×
714
                if err != nil {
×
715
                        return "", "", "", "", err
×
716
                }
×
717
                if isSASToken(key) {
×
718
                        accountSasToken = key
×
719
                } else {
×
720
                        accountKey = key
×
721
                }
×
722
        } else {
2✔
723
                if len(secrets) == 0 {
4✔
724
                        var rgName string
2✔
725
                        rgName, accountName, containerName, _, _, err = GetContainerInfo(volumeID)
2✔
726
                        if err != nil {
2✔
727
                                return "", "", "", "", err
×
728
                        }
×
729

730
                        if rgName == "" {
2✔
731
                                rgName = d.cloud.ResourceGroup
×
732
                        }
×
733

734
                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
2✔
735
                        if err != nil {
3✔
736
                                return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
1✔
737
                        }
1✔
738
                }
739
        }
740

741
        if containerName == "" {
1✔
742
                return "", "", "", "", fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
743
        }
×
744

745
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
746
}
747

748
func IsCorruptedDir(dir string) bool {
4✔
749
        _, pathErr := mount.PathExists(dir)
4✔
750
        return pathErr != nil && mount.IsCorruptedMnt(pathErr)
4✔
751
}
4✔
752

753
func isRetriableError(err error) bool {
5✔
754
        if err != nil {
9✔
755
                for _, v := range retriableErrors {
19✔
756
                        if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(v)) {
18✔
757
                                return true
3✔
758
                        }
3✔
759
                }
760
        }
761
        return false
2✔
762
}
763

764
func isSupportedProtocol(protocol string) bool {
19✔
765
        if protocol == "" {
20✔
766
                return true
1✔
767
        }
1✔
768
        for _, v := range supportedProtocolList {
46✔
769
                if protocol == v || protocol == NFSv3 {
44✔
770
                        return true
16✔
771
                }
16✔
772
        }
773
        return false
2✔
774
}
775

776
func isSupportedAccessTier(accessTier string) bool {
22✔
777
        if accessTier == "" {
37✔
778
                return true
15✔
779
        }
15✔
780
        for _, tier := range armstorage.PossibleAccessTierValues() {
32✔
781
                if accessTier == string(tier) {
28✔
782
                        return true
3✔
783
                }
3✔
784
        }
785
        return false
4✔
786
}
787

788
// container names can contain only lowercase letters, numbers, and hyphens,
789
// and must begin and end with a letter or a number
790
func isSupportedContainerNamePrefix(prefix string) bool {
21✔
791
        if prefix == "" {
34✔
792
                return true
13✔
793
        }
13✔
794
        if len(prefix) > 20 {
9✔
795
                return false
1✔
796
        }
1✔
797
        if prefix[0] == '-' {
8✔
798
                return false
1✔
799
        }
1✔
800
        for _, v := range prefix {
19✔
801
                if v != '-' && (v < '0' || v > '9') && (v < 'a' || v > 'z') {
17✔
802
                        return false
4✔
803
                }
4✔
804
        }
805
        return true
2✔
806
}
807

808
// isNFSProtocol checks if the protocol is NFS or AZNFS
809
func isNFSProtocol(protocol string) bool {
20✔
810
        protocol = strings.ToLower(protocol)
20✔
811
        return protocol == NFS || protocol == AZNFS || protocol == NFSv3
20✔
812
}
20✔
813

814
// get storage account from secrets map
815
func getStorageAccount(secrets map[string]string) (string, string, error) {
22✔
816
        if secrets == nil {
23✔
817
                return "", "", fmt.Errorf("unexpected: getStorageAccount secrets is nil")
1✔
818
        }
1✔
819

820
        var accountName, accountKey string
21✔
821
        for k, v := range secrets {
64✔
822
                v = strings.TrimSpace(v)
43✔
823
                switch strings.ToLower(k) {
43✔
824
                case accountNameField:
7✔
825
                        accountName = v
7✔
826
                case defaultSecretAccountName: // for compatibility with built-in azurefile plugin
13✔
827
                        accountName = v
13✔
828
                case accountKeyField:
7✔
829
                        accountKey = v
7✔
830
                case defaultSecretAccountKey: // for compatibility with built-in azurefile plugin
12✔
831
                        accountKey = v
12✔
832
                }
833
        }
834

835
        if accountName == "" {
25✔
836
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountNameField, defaultSecretAccountName)
4✔
837
        }
4✔
838
        if accountKey == "" {
21✔
839
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountKeyField, defaultSecretAccountKey)
4✔
840
        }
4✔
841

842
        accountName = strings.TrimSpace(accountName)
13✔
843
        klog.V(4).Infof("got storage account(%s) from secret", accountName)
13✔
844
        return accountName, accountKey, nil
13✔
845
}
846

847
func getContainerReference(containerName string, secrets map[string]string, env az.Environment) (*azstorage.Container, error) {
9✔
848
        accountName, accountKey, rerr := getStorageAccount(secrets)
9✔
849
        if rerr != nil {
11✔
850
                return nil, rerr
2✔
851
        }
2✔
852
        client, err := azstorage.NewBasicClientOnSovereignCloud(accountName, accountKey, env)
7✔
853
        if err != nil {
13✔
854
                return nil, err
6✔
855
        }
6✔
856
        blobClient := client.GetBlobService()
1✔
857
        container := blobClient.GetContainerReference(containerName)
1✔
858
        if container == nil {
1✔
859
                return nil, fmt.Errorf("ContainerReference of %s is nil", containerName)
×
860
        }
×
861
        return container, nil
1✔
862
}
863

864
func setAzureCredentials(ctx context.Context, kubeClient kubernetes.Interface, accountName, accountKey, secretNamespace string) (string, error) {
6✔
865
        if kubeClient == nil {
8✔
866
                klog.Warningf("could not create secret: kubeClient is nil")
2✔
867
                return "", nil
2✔
868
        }
2✔
869
        if accountName == "" || accountKey == "" {
6✔
870
                return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey)
2✔
871
        }
2✔
872
        secretName := fmt.Sprintf(secretNameTemplate, accountName)
2✔
873
        secret := &v1.Secret{
2✔
874
                ObjectMeta: metav1.ObjectMeta{
2✔
875
                        Namespace: secretNamespace,
2✔
876
                        Name:      secretName,
2✔
877
                },
2✔
878
                Data: map[string][]byte{
2✔
879
                        defaultSecretAccountName: []byte(accountName),
2✔
880
                        defaultSecretAccountKey:  []byte(accountKey),
2✔
881
                },
2✔
882
                Type: "Opaque",
2✔
883
        }
2✔
884
        _, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(ctx, secret, metav1.CreateOptions{})
2✔
885
        if apierror.IsAlreadyExists(err) {
3✔
886
                err = nil
1✔
887
        }
1✔
888
        if err != nil {
2✔
889
                return "", fmt.Errorf("couldn't create secret %w", err)
×
890
        }
×
891
        return secretName, err
2✔
892
}
893

894
// GetStorageAccesskey get Azure storage account key from
895
//  1. secrets (if not empty)
896
//  2. use k8s client identity to read from k8s secret
897
//  3. use cluster identity to get from storage account directly
898
func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure.AccountOptions, secrets map[string]string, secretName, secretNamespace string) (string, string, error) {
11✔
899
        if len(secrets) > 0 {
16✔
900
                return getStorageAccount(secrets)
5✔
901
        }
5✔
902

903
        // read from k8s secret first
904
        if secretName == "" {
10✔
905
                secretName = fmt.Sprintf(secretNameTemplate, accountOptions.Name)
4✔
906
        }
4✔
907
        _, accountKey, _, _, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint
6✔
908
        if err != nil {
10✔
909
                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✔
910
                accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup, accountOptions.GetLatestAccountKey)
4✔
911
        }
4✔
912
        return accountOptions.Name, accountKey, err
6✔
913
}
914

915
// GetInfoFromSecret get info from k8s secret
916
// return <accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, error>
917
func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, string, string, error) {
15✔
918
        if d.KubeClient == nil {
24✔
919
                return "", "", "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName)
9✔
920
        }
9✔
921

922
        secret, err := d.KubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
6✔
923
        if err != nil {
8✔
924
                return "", "", "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err)
2✔
925
        }
2✔
926

927
        accountName := strings.TrimSpace(string(secret.Data[defaultSecretAccountName][:]))
4✔
928
        accountKey := strings.TrimSpace(string(secret.Data[defaultSecretAccountKey][:]))
4✔
929
        accountSasToken := strings.TrimSpace(string(secret.Data[accountSasTokenField][:]))
4✔
930
        msiSecret := strings.TrimSpace(string(secret.Data[msiSecretField][:]))
4✔
931
        spnClientSecret := strings.TrimSpace(string(secret.Data[storageSPNClientSecretField][:]))
4✔
932
        spnClientID := strings.TrimSpace(string(secret.Data[storageSPNClientIDField][:]))
4✔
933
        spnTenantID := strings.TrimSpace(string(secret.Data[storageSPNTenantIDField][:]))
4✔
934

4✔
935
        klog.V(4).Infof("got storage account(%s) from secret(%s) namespace(%s)", accountName, secretName, secretNamespace)
4✔
936
        return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil
4✔
937
}
938

939
// getSubnetResourceID get default subnet resource ID from cloud provider config
940
func (d *Driver) getSubnetResourceID(vnetResourceGroup, vnetName, subnetName string) string {
6✔
941
        subsID := d.cloud.SubscriptionID
6✔
942
        if len(d.cloud.NetworkResourceSubscriptionID) > 0 {
10✔
943
                subsID = d.cloud.NetworkResourceSubscriptionID
4✔
944
        }
4✔
945

946
        if len(vnetResourceGroup) == 0 {
11✔
947
                vnetResourceGroup = d.cloud.ResourceGroup
5✔
948
                if len(d.cloud.VnetResourceGroup) > 0 {
8✔
949
                        vnetResourceGroup = d.cloud.VnetResourceGroup
3✔
950
                }
3✔
951
        }
952

953
        if len(vnetName) == 0 {
11✔
954
                vnetName = d.cloud.VnetName
5✔
955
        }
5✔
956

957
        if len(subnetName) == 0 {
11✔
958
                subnetName = d.cloud.SubnetName
5✔
959
        }
5✔
960
        return fmt.Sprintf(subnetTemplate, subsID, vnetResourceGroup, vnetName, subnetName)
6✔
961
}
962

963
func (d *Driver) useDataPlaneAPI(volumeID, accountName string) bool {
10✔
964
        cache, err := d.dataPlaneAPIVolCache.Get(volumeID, azcache.CacheReadTypeDefault)
10✔
965
        if err != nil {
10✔
966
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", volumeID, err)
×
967
        }
×
968
        if cache != nil {
13✔
969
                return true
3✔
970
        }
3✔
971
        cache, err = d.dataPlaneAPIVolCache.Get(accountName, azcache.CacheReadTypeDefault)
7✔
972
        if err != nil {
7✔
973
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", accountName, err)
×
974
        }
×
975
        if cache != nil {
7✔
976
                return true
×
977
        }
×
978
        return false
7✔
979
}
980

981
// appendDefaultMountOptions return mount options combined with mountOptions and defaultMountOptions
982
func appendDefaultMountOptions(mountOptions []string, tmpPath, containerName string) []string {
4✔
983
        var defaultMountOptions = map[string]string{
4✔
984
                "--pre-mount-validate": "true",
4✔
985
                "--use-https":          "true",
4✔
986
                "--tmp-path":           tmpPath,
4✔
987
                "--container-name":     containerName,
4✔
988
                // prevent billing charges on mounting
4✔
989
                "--cancel-list-on-mount-seconds": "10",
4✔
990
                // allow remounting using a non-empty tmp-path
4✔
991
                "--empty-dir-check": "false",
4✔
992
        }
4✔
993

4✔
994
        // stores the mount options already included in mountOptions
4✔
995
        included := make(map[string]bool)
4✔
996

4✔
997
        for _, mountOption := range mountOptions {
11✔
998
                for k := range defaultMountOptions {
49✔
999
                        if strings.HasPrefix(mountOption, k) {
46✔
1000
                                included[k] = true
4✔
1001
                        }
4✔
1002
                }
1003
        }
1004

1005
        allMountOptions := mountOptions
4✔
1006

4✔
1007
        for k, v := range defaultMountOptions {
28✔
1008
                if _, isIncluded := included[k]; !isIncluded {
44✔
1009
                        if v != "" {
40✔
1010
                                allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", k, v))
20✔
1011
                        } else {
20✔
1012
                                allMountOptions = append(allMountOptions, k)
×
1013
                        }
×
1014
                }
1015
        }
1016

1017
        return allMountOptions
4✔
1018
}
1019

1020
// chmodIfPermissionMismatch only perform chmod when permission mismatches
1021
func chmodIfPermissionMismatch(targetPath string, mode os.FileMode) error {
3✔
1022
        info, err := os.Lstat(targetPath)
3✔
1023
        if err != nil {
4✔
1024
                return err
1✔
1025
        }
1✔
1026
        perm := info.Mode() & os.ModePerm
2✔
1027
        if perm != mode {
3✔
1028
                klog.V(2).Infof("chmod targetPath(%s, mode:0%o) with permissions(0%o)", targetPath, info.Mode(), mode)
1✔
1029
                if err := os.Chmod(targetPath, mode); err != nil {
1✔
1030
                        return err
×
1031
                }
×
1032
        } else {
1✔
1033
                klog.V(2).Infof("skip chmod on targetPath(%s) since mode is already 0%o)", targetPath, info.Mode())
1✔
1034
        }
1✔
1035
        return nil
2✔
1036
}
1037

1038
func createStorageAccountSecret(account, key string) map[string]string {
1✔
1039
        secret := make(map[string]string)
1✔
1040
        secret[defaultSecretAccountName] = account
1✔
1041
        secret[defaultSecretAccountKey] = key
1✔
1042
        return secret
1✔
1043
}
1✔
1044

1045
// setKeyValueInMap set key/value pair in map
1046
// key in the map is case insensitive, if key already exists, overwrite existing value
1047
func setKeyValueInMap(m map[string]string, key, value string) {
6✔
1048
        if m == nil {
7✔
1049
                return
1✔
1050
        }
1✔
1051
        for k := range m {
16✔
1052
                if strings.EqualFold(k, key) {
13✔
1053
                        m[k] = value
2✔
1054
                        return
2✔
1055
                }
2✔
1056
        }
1057
        m[key] = value
3✔
1058
}
1059

1060
// getValueInMap get value from map by key
1061
// key in the map is case insensitive
1062
func getValueInMap(m map[string]string, key string) string {
12✔
1063
        if m == nil {
13✔
1064
                return ""
1✔
1065
        }
1✔
1066
        for k, v := range m {
23✔
1067
                if strings.EqualFold(k, key) {
16✔
1068
                        return v
4✔
1069
                }
4✔
1070
        }
1071
        return ""
7✔
1072
}
1073

1074
// replaceWithMap replace key with value for str
1075
func replaceWithMap(str string, m map[string]string) string {
13✔
1076
        for k, v := range m {
18✔
1077
                if k != "" {
9✔
1078
                        str = strings.ReplaceAll(str, k, v)
4✔
1079
                }
4✔
1080
        }
1081
        return str
13✔
1082
}
1083

1084
func isSupportedFSGroupChangePolicy(policy string) bool {
27✔
1085
        if policy == "" {
47✔
1086
                return true
20✔
1087
        }
20✔
1088
        for _, v := range supportedFSGroupChangePolicyList {
25✔
1089
                if policy == v {
21✔
1090
                        return true
3✔
1091
                }
3✔
1092
        }
1093
        return false
4✔
1094
}
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