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

kubernetes-sigs / blob-csi-driver / 8114056334

01 Mar 2024 04:16PM UTC coverage: 74.485%. Remained the same
8114056334

Pull #1267

github

web-flow
chore(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0

Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.4 to 1.9.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1267: chore(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0

2169 of 2912 relevant lines covered (74.48%)

7.23 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
        vnetResourceGroupField         = "vnetresourcegroup"
116
        vnetNameField                  = "vnetname"
117
        subnetNameField                = "subnetname"
118
        accessTierField                = "accesstier"
119
        networkEndpointTypeField       = "networkendpointtype"
120
        mountPermissionsField          = "mountpermissions"
121
        fsGroupChangePolicyField       = "fsgroupchangepolicy"
122
        useDataPlaneAPIField           = "usedataplaneapi"
123

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

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

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

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

141
        defaultNamespace = "default"
142

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

150
        VolumeID = "volumeid"
151

152
        defaultStorageEndPointSuffix = "core.windows.net"
153

154
        FSGroupChangeNone = "None"
155
)
156

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

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

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

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

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

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

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

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

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

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

134✔
337
        return &d
134✔
338
}
339

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

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

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

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

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

429
        return false
2✔
430
}
431

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1001
        allMountOptions := mountOptions
4✔
1002

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

1013
        return allMountOptions
4✔
1014
}
1015

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

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

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

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

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

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