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

kubernetes-sigs / blob-csi-driver / 8455121781

27 Mar 2024 04:19PM CUT coverage: 74.333%. Remained the same
8455121781

Pull #1317

github

web-flow
chore(deps): bump sigs.k8s.io/cloud-provider-azure/pkg/azclient

Bumps [sigs.k8s.io/cloud-provider-azure/pkg/azclient](https://github.com/kubernetes-sigs/cloud-provider-azure) from 0.0.4 to 0.0.5.
- [Release notes](https://github.com/kubernetes-sigs/cloud-provider-azure/releases)
- [Changelog](https://github.com/kubernetes-sigs/cloud-provider-azure/blob/master/docs/release-versioning.md)
- [Commits](https://github.com/kubernetes-sigs/cloud-provider-azure/compare/pkg/azclient/v0.0.4...pkg/azclient/v0.0.5)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/cloud-provider-azure/pkg/azclient
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1317: chore(deps): bump sigs.k8s.io/cloud-provider-azure/pkg/azclient from 0.0.4 to 0.0.5

2201 of 2961 relevant lines covered (74.33%)

7.21 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
        requireInfraEncryptionField    = "requireinfraencryption"
98
        ephemeralField                 = "csi.storage.k8s.io/ephemeral"
99
        podNamespaceField              = "csi.storage.k8s.io/pod.namespace"
100
        serviceAccountTokenField       = "csi.storage.k8s.io/serviceAccount.tokens"
101
        clientIDField                  = "clientID"
102
        tenantIDField                  = "tenantID"
103
        mountOptionsField              = "mountoptions"
104
        falseValue                     = "false"
105
        trueValue                      = "true"
106
        defaultSecretAccountName       = "azurestorageaccountname"
107
        defaultSecretAccountKey        = "azurestorageaccountkey"
108
        accountSasTokenField           = "azurestorageaccountsastoken"
109
        msiSecretField                 = "msisecret"
110
        storageSPNClientSecretField    = "azurestoragespnclientsecret"
111
        Fuse                           = "fuse"
112
        Fuse2                          = "fuse2"
113
        NFS                            = "nfs"
114
        AZNFS                          = "aznfs"
115
        NFSv3                          = "nfsv3"
116
        vnetResourceGroupField         = "vnetresourcegroup"
117
        vnetNameField                  = "vnetname"
118
        subnetNameField                = "subnetname"
119
        accessTierField                = "accesstier"
120
        networkEndpointTypeField       = "networkendpointtype"
121
        mountPermissionsField          = "mountpermissions"
122
        fsGroupChangePolicyField       = "fsgroupchangepolicy"
123
        useDataPlaneAPIField           = "usedataplaneapi"
124

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

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

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

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

142
        defaultNamespace = "default"
143

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

151
        VolumeID = "volumeid"
152

153
        defaultStorageEndPointSuffix = "core.windows.net"
154

155
        FSGroupChangeNone = "None"
156
)
157

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

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

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

205
// Driver implements all interfaces of CSI drivers
206
type Driver struct {
207
        csicommon.CSIDriver
208

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

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

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

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

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

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

135✔
338
        return &d
135✔
339
}
340

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

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

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

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

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

430
        return false
2✔
431
}
432

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

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

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

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

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

532
        if secretNamespace == "" {
13✔
533
                if pvcNamespace == "" {
12✔
534
                        secretNamespace = defaultNamespace
6✔
535
                } else {
6✔
536
                        secretNamespace = pvcNamespace
×
537
                }
×
538
        }
539

540
        if rgName == "" {
8✔
541
                rgName = d.cloud.ResourceGroup
1✔
542
        }
1✔
543

544
        if tenantID == "" {
14✔
545
                tenantID = d.cloud.TenantID
7✔
546
        }
7✔
547

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

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

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

634
        if accountKey != "" {
8✔
635
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
636
        }
4✔
637

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

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

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

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

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

663
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
664
}
665

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

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

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

727
                        if rgName == "" {
2✔
728
                                rgName = d.cloud.ResourceGroup
×
729
                        }
×
730

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

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

742
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
743
}
744

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

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

761
func isSupportedProtocol(protocol string) bool {
18✔
762
        if protocol == "" {
19✔
763
                return true
1✔
764
        }
1✔
765
        for _, v := range supportedProtocolList {
44✔
766
                if protocol == v || protocol == NFSv3 {
42✔
767
                        return true
15✔
768
                }
15✔
769
        }
770
        return false
2✔
771
}
772

773
func isSupportedAccessTier(accessTier string) bool {
21✔
774
        if accessTier == "" {
35✔
775
                return true
14✔
776
        }
14✔
777
        for _, tier := range armstorage.PossibleAccessTierValues() {
25✔
778
                if accessTier == string(tier) {
21✔
779
                        return true
3✔
780
                }
3✔
781
        }
782
        return false
4✔
783
}
784

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

805
// isNFSProtocol checks if the protocol is NFS or AZNFS
806
func isNFSProtocol(protocol string) bool {
21✔
807
        protocol = strings.ToLower(protocol)
21✔
808
        return protocol == NFS || protocol == AZNFS || protocol == NFSv3
21✔
809
}
21✔
810

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

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

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

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

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

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

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

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

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

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

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

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

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

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

950
        if len(vnetName) == 0 {
11✔
951
                vnetName = d.cloud.VnetName
5✔
952
        }
5✔
953

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

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

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

4✔
991
        // stores the mount options already included in mountOptions
4✔
992
        included := make(map[string]bool)
4✔
993

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

1002
        allMountOptions := mountOptions
4✔
1003

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

1014
        return allMountOptions
4✔
1015
}
1016

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

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

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

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

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

1081
func isSupportedFSGroupChangePolicy(policy string) bool {
28✔
1082
        if policy == "" {
49✔
1083
                return true
21✔
1084
        }
21✔
1085
        for _, v := range supportedFSGroupChangePolicyList {
25✔
1086
                if policy == v {
21✔
1087
                        return true
3✔
1088
                }
3✔
1089
        }
1090
        return false
4✔
1091
}
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