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

kubernetes-sigs / blob-csi-driver / 10208144261

02 Aug 2024 12:59AM UTC coverage: 73.338%. Remained the same
10208144261

Pull #1530

github

web-flow
chore(deps): bump github.com/onsi/gomega from 1.34.0 to 1.34.1

Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.34.0 to 1.34.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.34.0...v1.34.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1530: chore(deps): bump github.com/onsi/gomega from 1.34.0 to 1.34.1

2206 of 3008 relevant lines covered (73.34%)

7.03 hits per line

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

84.89
/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
        grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
35
        "github.com/pborman/uuid"
36
        "google.golang.org/grpc"
37
        v1 "k8s.io/api/core/v1"
38
        apierror "k8s.io/apimachinery/pkg/api/errors"
39
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40
        "k8s.io/client-go/kubernetes"
41
        "k8s.io/klog/v2"
42
        k8sutil "k8s.io/kubernetes/pkg/volume/util"
43
        mount "k8s.io/mount-utils"
44
        utilexec "k8s.io/utils/exec"
45

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

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

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

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

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

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

144
        defaultNamespace = "default"
145

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

153
        VolumeID = "volumeid"
154

155
        defaultStorageEndPointSuffix = "core.windows.net"
156

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

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

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

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

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

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

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

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

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

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

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

134✔
342
        return &d
134✔
343
}
344

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

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

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

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

427
func checkContainerNameBeginAndEnd(containerName string) bool {
12✔
428
        length := len(containerName)
12✔
429
        if (('a' <= containerName[0] && containerName[0] <= 'z') ||
12✔
430
                ('0' <= containerName[0] && containerName[0] <= '9')) &&
12✔
431
                (('a' <= containerName[length-1] && containerName[length-1] <= 'z') ||
12✔
432
                        ('0' <= containerName[length-1] && containerName[length-1] <= '9')) {
22✔
433
                return true
10✔
434
        }
10✔
435

436
        return false
2✔
437
}
438

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

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

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

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

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

538
        if secretNamespace == "" {
13✔
539
                if pvcNamespace == "" {
12✔
540
                        secretNamespace = defaultNamespace
6✔
541
                } else {
6✔
542
                        secretNamespace = pvcNamespace
×
543
                }
×
544
        }
545

546
        if rgName == "" {
8✔
547
                rgName = d.cloud.ResourceGroup
1✔
548
        }
1✔
549

550
        if tenantID == "" {
14✔
551
                tenantID = d.cloud.TenantID
7✔
552
        }
7✔
553

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

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

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

640
        if accountKey != "" {
8✔
641
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
642
        }
4✔
643

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

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

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

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

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

669
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
670
}
671

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

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

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

733
                        if rgName == "" {
2✔
734
                                rgName = d.cloud.ResourceGroup
×
735
                        }
×
736

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

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

748
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
749
}
750

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

956
        if len(vnetName) == 0 {
11✔
957
                vnetName = d.cloud.VnetName
5✔
958
        }
5✔
959

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

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

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

4✔
997
        // stores the mount options already included in mountOptions
4✔
998
        included := make(map[string]bool)
4✔
999

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

1008
        allMountOptions := mountOptions
4✔
1009

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

1020
        return allMountOptions
4✔
1021
}
1022

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

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

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

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

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

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