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

kubernetes-sigs / blob-csi-driver / 11841847083

14 Nov 2024 04:55PM UTC coverage: 73.558%. Remained the same
11841847083

Pull #1702

github

web-flow
chore(deps): bump google.golang.org/protobuf from 1.35.1 to 1.35.2

Bumps google.golang.org/protobuf from 1.35.1 to 1.35.2.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1702: chore(deps): bump google.golang.org/protobuf from 1.35.1 to 1.35.2

2245 of 3052 relevant lines covered (73.56%)

7.13 hits per line

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

84.87
/pkg/blob/blob.go
1
/*
2
Copyright 2019 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package blob
18

19
import (
20
        "context"
21
        "errors"
22
        "flag"
23
        "fmt"
24
        "os"
25
        "strconv"
26
        "strings"
27
        "sync"
28
        "time"
29

30
        "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
31
        azstorage "github.com/Azure/azure-sdk-for-go/storage"
32
        az "github.com/Azure/go-autorest/autorest/azure"
33
        "github.com/container-storage-interface/spec/lib/go/csi"
34
        grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
35
        "github.com/pborman/uuid"
36
        "google.golang.org/grpc"
37
        v1 "k8s.io/api/core/v1"
38
        apierror "k8s.io/apimachinery/pkg/api/errors"
39
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40
        "k8s.io/client-go/kubernetes"
41
        "k8s.io/klog/v2"
42
        k8sutil "k8s.io/kubernetes/pkg/volume/util"
43
        mount "k8s.io/mount-utils"
44
        utilexec "k8s.io/utils/exec"
45

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

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

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

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

139
        // containerMaxSize is the max size of the blob container. See https://docs.microsoft.com/en-us/azure/storage/blobs/scalability-targets#scale-targets-for-blob-storage
140
        containerMaxSize = 100 * util.TiB
141

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

144
        defaultNamespace = "default"
145

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

153
        VolumeID = "volumeid"
154

155
        defaultStorageEndPointSuffix = "core.windows.net"
156

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

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

167
        // azcopyCloneVolumeOptions used in volume cloning between different storage account and --check-length to false because volume data may be in changing state, copy volume is not same as current source volume,
168
        // set --s2s-preserve-access-tier=false to avoid BlobAccessTierNotSupportedForAccountType error in azcopy
169
        azcopyCloneVolumeOptions = []string{"--recursive", "--check-length=false", "--s2s-preserve-access-tier=false"}
170
)
171

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

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

213
// Driver implements all interfaces of CSI drivers
214
type Driver struct {
215
        csicommon.CSIDriver
216

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

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

296
        var err error
134✔
297
        getter := func(_ string) (interface{}, error) { return nil, nil }
157✔
298
        if d.accountSearchCache, err = azcache.NewTimedCache(time.Minute, getter, false); err != nil {
134✔
299
                klog.Fatalf("%v", err)
×
300
        }
×
301
        if d.dataPlaneAPIVolCache, err = azcache.NewTimedCache(24*30*time.Hour, getter, false); err != nil {
134✔
302
                klog.Fatalf("%v", err)
×
303
        }
×
304
        if d.azcopySasTokenCache, err = azcache.NewTimedCache(15*time.Minute, getter, false); err != nil {
134✔
305
                klog.Fatalf("%v", err)
×
306
        }
×
307

308
        if options.VolStatsCacheExpireInMinutes <= 0 {
268✔
309
                options.VolStatsCacheExpireInMinutes = 10 // default expire in 10 minutes
134✔
310
        }
134✔
311
        if d.volStatsCache, err = azcache.NewTimedCache(time.Duration(options.VolStatsCacheExpireInMinutes)*time.Minute, getter, false); err != nil {
134✔
312
                klog.Fatalf("%v", err)
×
313
        }
×
314
        if d.subnetCache, err = azcache.NewTimedCache(10*time.Minute, getter, false); err != nil {
134✔
315
                klog.Fatalf("%v", err)
×
316
        }
×
317

318
        d.mounter = &mount.SafeFormatAndMount{
134✔
319
                Interface: mount.New(""),
134✔
320
                Exec:      utilexec.New(),
134✔
321
        }
134✔
322

134✔
323
        // Initialize default library driver
134✔
324
        d.AddControllerServiceCapabilities(
134✔
325
                []csi.ControllerServiceCapability_RPC_Type{
134✔
326
                        csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
134✔
327
                        //csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
134✔
328
                        //csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
134✔
329
                        csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
134✔
330
                        csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
134✔
331
                        csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
134✔
332
                })
134✔
333
        d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{
134✔
334
                csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
134✔
335
                csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
134✔
336
                csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
134✔
337
                csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
134✔
338
                csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
134✔
339
                csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER,
134✔
340
                csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
134✔
341
        })
134✔
342

134✔
343
        nodeCap := []csi.NodeServiceCapability_RPC_Type{
134✔
344
                csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
134✔
345
                csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
134✔
346
        }
134✔
347
        if d.enableGetVolumeStats {
134✔
348
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
×
349
        }
×
350
        if d.enableVolumeMountGroup {
134✔
351
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_VOLUME_MOUNT_GROUP)
×
352
        }
×
353
        d.AddNodeServiceCapabilities(nodeCap)
134✔
354

134✔
355
        return &d
134✔
356
}
357

358
// Run driver initialization
359
func (d *Driver) Run(ctx context.Context, endpoint string) error {
2✔
360
        versionMeta, err := GetVersionYAML(d.Name)
2✔
361
        if err != nil {
2✔
362
                klog.Fatalf("%v", err)
×
363
        }
×
364
        klog.Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta)
2✔
365
        opts := []grpc.ServerOption{
2✔
366
                grpc.ChainUnaryInterceptor(
2✔
367
                        grpcprom.NewServerMetrics().UnaryServerInterceptor(),
2✔
368
                        csicommon.LogGRPC,
2✔
369
                ),
2✔
370
        }
2✔
371
        s := grpc.NewServer(opts...)
2✔
372
        csi.RegisterIdentityServer(s, d)
2✔
373
        csi.RegisterControllerServer(s, d)
2✔
374
        csi.RegisterNodeServer(s, d)
2✔
375

2✔
376
        go func() {
4✔
377
                //graceful shutdown
2✔
378
                <-ctx.Done()
2✔
379
                s.GracefulStop()
2✔
380
        }()
2✔
381
        // Driver d act as IdentityServer, ControllerServer and NodeServer
382
        listener, err := csicommon.Listen(ctx, endpoint)
2✔
383
        if err != nil {
2✔
384
                klog.Fatalf("failed to listen to endpoint, error: %v", err)
×
385
        }
×
386
        err = s.Serve(listener)
2✔
387
        if errors.Is(err, grpc.ErrServerStopped) {
2✔
388
                klog.Infof("gRPC server stopped serving")
×
389
                return nil
×
390
        }
×
391
        return err
2✔
392
}
393

394
// GetContainerInfo get container info according to volume id
395
// the format of VolumeId is: rg#accountName#containerName#uuid#secretNamespace#subsID
396
//
397
// e.g.
398
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#"
399
// output: rg, f5713de20cde511e8ba4900, containerName, "" , ""
400
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#"
401
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, ""
402
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#subsID"
403
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, subsID
404
func GetContainerInfo(id string) (string, string, string, string, string, error) {
40✔
405
        segments := strings.Split(id, separator)
40✔
406
        if len(segments) < 3 {
49✔
407
                return "", "", "", "", "", fmt.Errorf("error parsing volume id: %q, should at least contain two #", id)
9✔
408
        }
9✔
409
        var secretNamespace, subsID string
31✔
410
        if len(segments) > 4 {
37✔
411
                secretNamespace = segments[4]
6✔
412
        }
6✔
413
        if len(segments) > 5 {
34✔
414
                subsID = segments[5]
3✔
415
        }
3✔
416
        return segments[0], segments[1], segments[2], secretNamespace, subsID, nil
31✔
417
}
418

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

440
func checkContainerNameBeginAndEnd(containerName string) bool {
12✔
441
        length := len(containerName)
12✔
442
        if (('a' <= containerName[0] && containerName[0] <= 'z') ||
12✔
443
                ('0' <= containerName[0] && containerName[0] <= '9')) &&
12✔
444
                (('a' <= containerName[length-1] && containerName[length-1] <= 'z') ||
12✔
445
                        ('0' <= containerName[length-1] && containerName[length-1] <= '9')) {
22✔
446
                return true
10✔
447
        }
10✔
448

449
        return false
2✔
450
}
451

452
// isSASToken checks if the key contains the patterns.
453
// SAS token format could refer to https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
454
func isSASToken(key string) bool {
3✔
455
        return strings.HasPrefix(key, "?")
3✔
456
}
3✔
457

458
// GetAuthEnv return <accountName, containerName, authEnv, error>
459
func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attrib, secrets map[string]string) (string, string, string, string, []string, error) {
10✔
460
        rgName, accountName, containerName, secretNamespace, _, err := GetContainerInfo(volumeID)
10✔
461
        if err != nil {
12✔
462
                // ignore volumeID parsing error
2✔
463
                klog.V(2).Infof("parsing volumeID(%s) return with error: %v", volumeID, err)
2✔
464
                err = nil
2✔
465
        }
2✔
466

467
        var (
10✔
468
                subsID                  string
10✔
469
                accountKey              string
10✔
470
                accountSasToken         string
10✔
471
                msiSecret               string
10✔
472
                storageSPNClientSecret  string
10✔
473
                storageSPNClientID      string
10✔
474
                storageSPNTenantID      string
10✔
475
                secretName              string
10✔
476
                pvcNamespace            string
10✔
477
                keyVaultURL             string
10✔
478
                keyVaultSecretName      string
10✔
479
                keyVaultSecretVersion   string
10✔
480
                azureStorageAuthType    string
10✔
481
                authEnv                 []string
10✔
482
                getAccountKeyFromSecret bool
10✔
483
                getLatestAccountKey     bool
10✔
484
                clientID                string
10✔
485
                tenantID                string
10✔
486
                serviceAccountToken     string
10✔
487
        )
10✔
488

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

9✔
546
        if protocol == NFS {
11✔
547
                // nfs protocol does not need account key, return directly
2✔
548
                return rgName, accountName, accountKey, containerName, authEnv, err
2✔
549
        }
2✔
550

551
        if secretNamespace == "" {
13✔
552
                if pvcNamespace == "" {
12✔
553
                        secretNamespace = defaultNamespace
6✔
554
                } else {
6✔
555
                        secretNamespace = pvcNamespace
×
556
                }
×
557
        }
558

559
        if rgName == "" {
8✔
560
                rgName = d.cloud.ResourceGroup
1✔
561
        }
1✔
562

563
        if tenantID == "" {
14✔
564
                tenantID = d.cloud.TenantID
7✔
565
        }
7✔
566

567
        // if client id is specified, we only use service account token to get account key
568
        if clientID != "" {
7✔
569
                klog.V(2).Infof("clientID(%s) is specified, use service account token to get account key", clientID)
×
570
                if subsID == "" {
×
571
                        subsID = d.cloud.SubscriptionID
×
572
                }
×
573
                accountKey, err := d.cloud.GetStorageAccesskeyFromServiceAccountToken(ctx, subsID, accountName, rgName, clientID, tenantID, serviceAccountToken)
×
574
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
×
575
                return rgName, accountName, accountKey, containerName, authEnv, err
×
576
        }
577

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

649
        if containerName == "" {
4✔
650
                err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
651
        }
×
652

653
        if accountKey != "" {
8✔
654
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
655
        }
4✔
656

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

662
        if msiSecret != "" {
5✔
663
                klog.V(2).Infof("msiSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
664
                authEnv = append(authEnv, "MSI_SECRET="+msiSecret)
1✔
665
        }
1✔
666

667
        if storageSPNClientSecret != "" {
5✔
668
                klog.V(2).Infof("storageSPNClientSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
669
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+storageSPNClientSecret)
1✔
670
        }
1✔
671

672
        if storageSPNClientID != "" {
4✔
673
                klog.V(2).Infof("storageSPNClientID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNClientID, accountName, containerName)
×
674
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+storageSPNClientID)
×
675
        }
×
676

677
        if storageSPNTenantID != "" {
4✔
678
                klog.V(2).Infof("storageSPNTenantID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNTenantID, accountName, containerName)
×
679
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+storageSPNTenantID)
×
680
        }
×
681

682
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
683
}
684

685
// GetStorageAccountAndContainer get storage account and container info
686
// returns <accountName, accountKey, accountSasToken, containerName>
687
// only for e2e testing
688
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
3✔
689
        var (
3✔
690
                subsID                string
3✔
691
                accountName           string
3✔
692
                accountKey            string
3✔
693
                accountSasToken       string
3✔
694
                containerName         string
3✔
695
                keyVaultURL           string
3✔
696
                keyVaultSecretName    string
3✔
697
                keyVaultSecretVersion string
3✔
698
                getLatestAccountKey   bool
3✔
699
                err                   error
3✔
700
        )
3✔
701

3✔
702
        for k, v := range attrib {
8✔
703
                switch strings.ToLower(k) {
5✔
704
                case subscriptionIDField:
×
705
                        subsID = v
×
706
                case containerNameField:
1✔
707
                        containerName = v
1✔
708
                case keyVaultURLField:
×
709
                        keyVaultURL = v
×
710
                case keyVaultSecretNameField:
1✔
711
                        keyVaultSecretName = v
1✔
712
                case keyVaultSecretVersionField:
1✔
713
                        keyVaultSecretVersion = v
1✔
714
                case storageAccountField:
×
715
                        accountName = v
×
716
                case storageAccountNameField: // for compatibility
1✔
717
                        accountName = v
1✔
718
                case getLatestAccountKeyField:
1✔
719
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
720
                                return "", "", "", "", fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
721
                        }
1✔
722
                }
723
        }
724

725
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
726
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
727
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
728
        if keyVaultURL != "" {
2✔
729
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
×
730
                if err != nil {
×
731
                        return "", "", "", "", err
×
732
                }
×
733
                if isSASToken(key) {
×
734
                        accountSasToken = key
×
735
                } else {
×
736
                        accountKey = key
×
737
                }
×
738
        } else {
2✔
739
                if len(secrets) == 0 {
4✔
740
                        var rgName string
2✔
741
                        rgName, accountName, containerName, _, _, err = GetContainerInfo(volumeID)
2✔
742
                        if err != nil {
2✔
743
                                return "", "", "", "", err
×
744
                        }
×
745

746
                        if rgName == "" {
2✔
747
                                rgName = d.cloud.ResourceGroup
×
748
                        }
×
749

750
                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
2✔
751
                        if err != nil {
3✔
752
                                return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
1✔
753
                        }
1✔
754
                }
755
        }
756

757
        if containerName == "" {
1✔
758
                return "", "", "", "", fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
759
        }
×
760

761
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
762
}
763

764
func IsCorruptedDir(dir string) bool {
4✔
765
        _, pathErr := mount.PathExists(dir)
4✔
766
        return pathErr != nil && mount.IsCorruptedMnt(pathErr)
4✔
767
}
4✔
768

769
func isRetriableError(err error) bool {
5✔
770
        if err != nil {
9✔
771
                for _, v := range retriableErrors {
19✔
772
                        if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(v)) {
18✔
773
                                return true
3✔
774
                        }
3✔
775
                }
776
        }
777
        return false
2✔
778
}
779

780
func isSupportedProtocol(protocol string) bool {
19✔
781
        if protocol == "" {
20✔
782
                return true
1✔
783
        }
1✔
784
        for _, v := range supportedProtocolList {
46✔
785
                if protocol == v || protocol == NFSv3 {
44✔
786
                        return true
16✔
787
                }
16✔
788
        }
789
        return false
2✔
790
}
791

792
func isSupportedAccessTier(accessTier string) bool {
22✔
793
        if accessTier == "" {
37✔
794
                return true
15✔
795
        }
15✔
796
        for _, tier := range armstorage.PossibleAccessTierValues() {
32✔
797
                if accessTier == string(tier) {
28✔
798
                        return true
3✔
799
                }
3✔
800
        }
801
        return false
4✔
802
}
803

804
// container names can contain only lowercase letters, numbers, and hyphens,
805
// and must begin and end with a letter or a number
806
func isSupportedContainerNamePrefix(prefix string) bool {
21✔
807
        if prefix == "" {
34✔
808
                return true
13✔
809
        }
13✔
810
        if len(prefix) > 20 {
9✔
811
                return false
1✔
812
        }
1✔
813
        if prefix[0] == '-' {
8✔
814
                return false
1✔
815
        }
1✔
816
        for _, v := range prefix {
19✔
817
                if v != '-' && (v < '0' || v > '9') && (v < 'a' || v > 'z') {
17✔
818
                        return false
4✔
819
                }
4✔
820
        }
821
        return true
2✔
822
}
823

824
// isNFSProtocol checks if the protocol is NFS or AZNFS
825
func isNFSProtocol(protocol string) bool {
20✔
826
        protocol = strings.ToLower(protocol)
20✔
827
        return protocol == NFS || protocol == AZNFS || protocol == NFSv3
20✔
828
}
20✔
829

830
// get storage account from secrets map
831
func getStorageAccount(secrets map[string]string) (string, string, error) {
22✔
832
        if secrets == nil {
23✔
833
                return "", "", fmt.Errorf("unexpected: getStorageAccount secrets is nil")
1✔
834
        }
1✔
835

836
        var accountName, accountKey string
21✔
837
        for k, v := range secrets {
64✔
838
                v = strings.TrimSpace(v)
43✔
839
                switch strings.ToLower(k) {
43✔
840
                case accountNameField:
7✔
841
                        accountName = v
7✔
842
                case defaultSecretAccountName: // for compatibility with built-in azurefile plugin
13✔
843
                        accountName = v
13✔
844
                case accountKeyField:
7✔
845
                        accountKey = v
7✔
846
                case defaultSecretAccountKey: // for compatibility with built-in azurefile plugin
12✔
847
                        accountKey = v
12✔
848
                }
849
        }
850

851
        if accountName == "" {
25✔
852
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountNameField, defaultSecretAccountName)
4✔
853
        }
4✔
854
        if accountKey == "" {
21✔
855
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountKeyField, defaultSecretAccountKey)
4✔
856
        }
4✔
857

858
        accountName = strings.TrimSpace(accountName)
13✔
859
        klog.V(4).Infof("got storage account(%s) from secret", accountName)
13✔
860
        return accountName, accountKey, nil
13✔
861
}
862

863
func getContainerReference(containerName string, secrets map[string]string, env az.Environment) (*azstorage.Container, error) {
9✔
864
        accountName, accountKey, rerr := getStorageAccount(secrets)
9✔
865
        if rerr != nil {
11✔
866
                return nil, rerr
2✔
867
        }
2✔
868
        client, err := azstorage.NewBasicClientOnSovereignCloud(accountName, accountKey, env)
7✔
869
        if err != nil {
13✔
870
                return nil, err
6✔
871
        }
6✔
872
        blobClient := client.GetBlobService()
1✔
873
        container := blobClient.GetContainerReference(containerName)
1✔
874
        if container == nil {
1✔
875
                return nil, fmt.Errorf("ContainerReference of %s is nil", containerName)
×
876
        }
×
877
        return container, nil
1✔
878
}
879

880
func setAzureCredentials(ctx context.Context, kubeClient kubernetes.Interface, accountName, accountKey, secretNamespace string) (string, error) {
6✔
881
        if kubeClient == nil {
8✔
882
                klog.Warningf("could not create secret: kubeClient is nil")
2✔
883
                return "", nil
2✔
884
        }
2✔
885
        if accountName == "" || accountKey == "" {
6✔
886
                return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey)
2✔
887
        }
2✔
888
        secretName := fmt.Sprintf(secretNameTemplate, accountName)
2✔
889
        secret := &v1.Secret{
2✔
890
                ObjectMeta: metav1.ObjectMeta{
2✔
891
                        Namespace: secretNamespace,
2✔
892
                        Name:      secretName,
2✔
893
                },
2✔
894
                Data: map[string][]byte{
2✔
895
                        defaultSecretAccountName: []byte(accountName),
2✔
896
                        defaultSecretAccountKey:  []byte(accountKey),
2✔
897
                },
2✔
898
                Type: "Opaque",
2✔
899
        }
2✔
900
        _, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(ctx, secret, metav1.CreateOptions{})
2✔
901
        if apierror.IsAlreadyExists(err) {
3✔
902
                err = nil
1✔
903
        }
1✔
904
        if err != nil {
2✔
905
                return "", fmt.Errorf("couldn't create secret %w", err)
×
906
        }
×
907
        return secretName, err
2✔
908
}
909

910
// GetStorageAccesskey get Azure storage account key from
911
//  1. secrets (if not empty)
912
//  2. use k8s client identity to read from k8s secret
913
//  3. use cluster identity to get from storage account directly
914
func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure.AccountOptions, secrets map[string]string, secretName, secretNamespace string) (string, string, error) {
12✔
915
        if len(secrets) > 0 {
17✔
916
                return getStorageAccount(secrets)
5✔
917
        }
5✔
918

919
        // read from k8s secret first
920
        if secretName == "" {
12✔
921
                secretName = fmt.Sprintf(secretNameTemplate, accountOptions.Name)
5✔
922
        }
5✔
923
        _, accountKey, _, _, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint
7✔
924
        if err != nil {
12✔
925
                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)
5✔
926
                accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup, accountOptions.GetLatestAccountKey)
5✔
927
        }
5✔
928
        return accountOptions.Name, accountKey, err
7✔
929
}
930

931
// GetInfoFromSecret get info from k8s secret
932
// return <accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, error>
933
func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, string, string, error) {
16✔
934
        if d.KubeClient == nil {
26✔
935
                return "", "", "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName)
10✔
936
        }
10✔
937

938
        secret, err := d.KubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
6✔
939
        if err != nil {
8✔
940
                return "", "", "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err)
2✔
941
        }
2✔
942

943
        accountName := strings.TrimSpace(string(secret.Data[defaultSecretAccountName][:]))
4✔
944
        accountKey := strings.TrimSpace(string(secret.Data[defaultSecretAccountKey][:]))
4✔
945
        accountSasToken := strings.TrimSpace(string(secret.Data[accountSasTokenField][:]))
4✔
946
        msiSecret := strings.TrimSpace(string(secret.Data[msiSecretField][:]))
4✔
947
        spnClientSecret := strings.TrimSpace(string(secret.Data[storageSPNClientSecretField][:]))
4✔
948
        spnClientID := strings.TrimSpace(string(secret.Data[storageSPNClientIDField][:]))
4✔
949
        spnTenantID := strings.TrimSpace(string(secret.Data[storageSPNTenantIDField][:]))
4✔
950

4✔
951
        klog.V(4).Infof("got storage account(%s) from secret(%s) namespace(%s)", accountName, secretName, secretNamespace)
4✔
952
        return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil
4✔
953
}
954

955
// getSubnetResourceID get default subnet resource ID from cloud provider config
956
func (d *Driver) getSubnetResourceID(vnetResourceGroup, vnetName, subnetName string) string {
6✔
957
        subsID := d.cloud.SubscriptionID
6✔
958
        if len(d.cloud.NetworkResourceSubscriptionID) > 0 {
10✔
959
                subsID = d.cloud.NetworkResourceSubscriptionID
4✔
960
        }
4✔
961

962
        if len(vnetResourceGroup) == 0 {
10✔
963
                vnetResourceGroup = d.cloud.ResourceGroup
4✔
964
                if len(d.cloud.VnetResourceGroup) > 0 {
7✔
965
                        vnetResourceGroup = d.cloud.VnetResourceGroup
3✔
966
                }
3✔
967
        }
968

969
        if len(vnetName) == 0 {
10✔
970
                vnetName = d.cloud.VnetName
4✔
971
        }
4✔
972

973
        if len(subnetName) == 0 {
10✔
974
                subnetName = d.cloud.SubnetName
4✔
975
        }
4✔
976
        return fmt.Sprintf(subnetTemplate, subsID, vnetResourceGroup, vnetName, subnetName)
6✔
977
}
978

979
func (d *Driver) useDataPlaneAPI(volumeID, accountName string) bool {
9✔
980
        cache, err := d.dataPlaneAPIVolCache.Get(volumeID, azcache.CacheReadTypeDefault)
9✔
981
        if err != nil {
9✔
982
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", volumeID, err)
×
983
        }
×
984
        if cache != nil {
12✔
985
                return true
3✔
986
        }
3✔
987
        cache, err = d.dataPlaneAPIVolCache.Get(accountName, azcache.CacheReadTypeDefault)
6✔
988
        if err != nil {
6✔
989
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", accountName, err)
×
990
        }
×
991
        if cache != nil {
6✔
992
                return true
×
993
        }
×
994
        return false
6✔
995
}
996

997
// appendDefaultMountOptions return mount options combined with mountOptions and defaultMountOptions
998
func appendDefaultMountOptions(mountOptions []string, tmpPath, containerName string) []string {
4✔
999
        var defaultMountOptions = map[string]string{
4✔
1000
                "--pre-mount-validate": "true",
4✔
1001
                "--use-https":          "true",
4✔
1002
                "--tmp-path":           tmpPath,
4✔
1003
                "--container-name":     containerName,
4✔
1004
                // prevent billing charges on mounting
4✔
1005
                "--cancel-list-on-mount-seconds": "10",
4✔
1006
                // allow remounting using a non-empty tmp-path
4✔
1007
                "--empty-dir-check": "false",
4✔
1008
        }
4✔
1009

4✔
1010
        // stores the mount options already included in mountOptions
4✔
1011
        included := make(map[string]bool)
4✔
1012

4✔
1013
        for _, mountOption := range mountOptions {
11✔
1014
                for k := range defaultMountOptions {
49✔
1015
                        if strings.HasPrefix(mountOption, k) {
46✔
1016
                                included[k] = true
4✔
1017
                        }
4✔
1018
                }
1019
        }
1020

1021
        allMountOptions := mountOptions
4✔
1022

4✔
1023
        for k, v := range defaultMountOptions {
28✔
1024
                if _, isIncluded := included[k]; !isIncluded {
44✔
1025
                        if v != "" {
40✔
1026
                                allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", k, v))
20✔
1027
                        } else {
20✔
1028
                                allMountOptions = append(allMountOptions, k)
×
1029
                        }
×
1030
                }
1031
        }
1032

1033
        return allMountOptions
4✔
1034
}
1035

1036
// chmodIfPermissionMismatch only perform chmod when permission mismatches
1037
func chmodIfPermissionMismatch(targetPath string, mode os.FileMode) error {
3✔
1038
        info, err := os.Lstat(targetPath)
3✔
1039
        if err != nil {
4✔
1040
                return err
1✔
1041
        }
1✔
1042
        perm := info.Mode() & os.ModePerm
2✔
1043
        if perm != mode {
3✔
1044
                klog.V(2).Infof("chmod targetPath(%s, mode:0%o) with permissions(0%o)", targetPath, info.Mode(), mode)
1✔
1045
                if err := os.Chmod(targetPath, mode); err != nil {
1✔
1046
                        return err
×
1047
                }
×
1048
        } else {
1✔
1049
                klog.V(2).Infof("skip chmod on targetPath(%s) since mode is already 0%o)", targetPath, info.Mode())
1✔
1050
        }
1✔
1051
        return nil
2✔
1052
}
1053

1054
func createStorageAccountSecret(account, key string) map[string]string {
1✔
1055
        secret := make(map[string]string)
1✔
1056
        secret[defaultSecretAccountName] = account
1✔
1057
        secret[defaultSecretAccountKey] = key
1✔
1058
        return secret
1✔
1059
}
1✔
1060

1061
// setKeyValueInMap set key/value pair in map
1062
// key in the map is case insensitive, if key already exists, overwrite existing value
1063
func setKeyValueInMap(m map[string]string, key, value string) {
7✔
1064
        if m == nil {
8✔
1065
                return
1✔
1066
        }
1✔
1067
        for k := range m {
17✔
1068
                if strings.EqualFold(k, key) {
13✔
1069
                        m[k] = value
2✔
1070
                        return
2✔
1071
                }
2✔
1072
        }
1073
        m[key] = value
4✔
1074
}
1075

1076
// getValueInMap get value from map by key
1077
// key in the map is case insensitive
1078
func getValueInMap(m map[string]string, key string) string {
12✔
1079
        if m == nil {
13✔
1080
                return ""
1✔
1081
        }
1✔
1082
        for k, v := range m {
23✔
1083
                if strings.EqualFold(k, key) {
16✔
1084
                        return v
4✔
1085
                }
4✔
1086
        }
1087
        return ""
7✔
1088
}
1089

1090
// replaceWithMap replace key with value for str
1091
func replaceWithMap(str string, m map[string]string) string {
15✔
1092
        for k, v := range m {
20✔
1093
                if k != "" {
9✔
1094
                        str = strings.ReplaceAll(str, k, v)
4✔
1095
                }
4✔
1096
        }
1097
        return str
15✔
1098
}
1099

1100
func isSupportedFSGroupChangePolicy(policy string) bool {
27✔
1101
        if policy == "" {
47✔
1102
                return true
20✔
1103
        }
20✔
1104
        for _, v := range supportedFSGroupChangePolicyList {
25✔
1105
                if policy == v {
21✔
1106
                        return true
3✔
1107
                }
3✔
1108
        }
1109
        return false
4✔
1110
}
1111

1112
func isReadOnlyFromCapability(vc *csi.VolumeCapability) bool {
7✔
1113
        if vc.GetAccessMode() == nil {
9✔
1114
                return false
2✔
1115
        }
2✔
1116
        mode := vc.GetAccessMode().GetMode()
5✔
1117
        return (mode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY ||
5✔
1118
                mode == csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY)
5✔
1119
}
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