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

kubernetes-sigs / blob-csi-driver / 13246414366

10 Feb 2025 05:09PM CUT coverage: 77.861%. Remained the same
13246414366

Pull #1831

github

web-flow
chore(deps): bump golang.org/x/net from 0.34.0 to 0.35.0

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.35.0.
- [Commits](https://github.com/golang/net/compare/v0.34.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1831: chore(deps): bump golang.org/x/net from 0.34.0 to 0.35.0

2286 of 2936 relevant lines covered (77.86%)

7.44 hits per line

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

85.06
/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
        mount "k8s.io/mount-utils"
43
        utilexec "k8s.io/utils/exec"
44

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

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

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

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

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

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

143
        defaultNamespace = "default"
144

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

152
        VolumeID = "volumeid"
153

154
        defaultStorageEndPointSuffix = "core.windows.net"
155

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

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

166
        // 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,
167
        // set --s2s-preserve-access-tier=false to avoid BlobAccessTierNotSupportedForAccountType error in azcopy
168
        azcopyCloneVolumeOptions = []string{"--recursive", "--check-length=false", "--s2s-preserve-access-tier=false"}
169
)
170

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

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

212
// Driver implements all interfaces of CSI drivers
213
type Driver struct {
214
        csicommon.CSIDriver
215
        // Embed UnimplementedXXXServer to ensure the driver returns Unimplemented for any
216
        // new RPC methods that might be introduced in future versions of the spec.
217
        csi.UnimplementedControllerServer
218
        csi.UnimplementedIdentityServer
219
        csi.UnimplementedNodeServer
220

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

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

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

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

322
        d.mounter = &mount.SafeFormatAndMount{
134✔
323
                Interface: mount.New(""),
134✔
324
                Exec:      utilexec.New(),
134✔
325
        }
134✔
326

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

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

134✔
359
        return &d
134✔
360
}
361

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

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

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

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

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

453
        return false
2✔
454
}
455

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

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

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

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

9✔
550
        if protocol == NFS {
11✔
551
                // nfs protocol does not need account key, return directly
2✔
552
                return rgName, accountName, accountKey, containerName, authEnv, err
2✔
553
        }
2✔
554

555
        if secretNamespace == "" {
13✔
556
                if pvcNamespace == "" {
12✔
557
                        secretNamespace = defaultNamespace
6✔
558
                } else {
6✔
559
                        secretNamespace = pvcNamespace
×
560
                }
×
561
        }
562

563
        if rgName == "" {
8✔
564
                rgName = d.cloud.ResourceGroup
1✔
565
        }
1✔
566

567
        if tenantID == "" {
14✔
568
                tenantID = d.cloud.TenantID
7✔
569
        }
7✔
570

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

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

653
        if containerName == "" {
4✔
654
                err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
655
        }
×
656

657
        if accountKey != "" {
8✔
658
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
659
        }
4✔
660

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

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

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

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

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

686
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
687
}
688

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

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

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

750
                        if rgName == "" {
2✔
751
                                rgName = d.cloud.ResourceGroup
×
752
                        }
×
753

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

761
        if containerName == "" {
1✔
762
                return "", "", "", "", fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
763
        }
×
764

765
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
766
}
767

768
func IsCorruptedDir(dir string) bool {
4✔
769
        _, pathErr := mount.PathExists(dir)
4✔
770
        return pathErr != nil && mount.IsCorruptedMnt(pathErr)
4✔
771
}
4✔
772

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

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

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

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

828
// isNFSProtocol checks if the protocol is NFS or AZNFS
829
func isNFSProtocol(protocol string) bool {
20✔
830
        protocol = strings.ToLower(protocol)
20✔
831
        return protocol == NFS || protocol == AZNFS || protocol == NFSv3
20✔
832
}
20✔
833

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

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

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

862
        accountName = strings.TrimSpace(accountName)
13✔
863
        klog.V(4).Infof("got storage account(%s) from secret", accountName)
13✔
864
        return accountName, accountKey, nil
13✔
865
}
866

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

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

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

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

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

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

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

4✔
955
        klog.V(4).Infof("got storage account(%s) from secret(%s) namespace(%s)", accountName, secretName, secretNamespace)
4✔
956
        return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil
4✔
957
}
958

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

966
        if len(vnetResourceGroup) == 0 {
10✔
967
                vnetResourceGroup = d.cloud.ResourceGroup
4✔
968
                if len(d.cloud.VnetResourceGroup) > 0 {
7✔
969
                        vnetResourceGroup = d.cloud.VnetResourceGroup
3✔
970
                }
3✔
971
        }
972

973
        if len(vnetName) == 0 {
10✔
974
                vnetName = d.cloud.VnetName
4✔
975
        }
4✔
976

977
        if len(subnetName) == 0 {
10✔
978
                subnetName = d.cloud.SubnetName
4✔
979
        }
4✔
980
        return fmt.Sprintf(subnetTemplate, subsID, vnetResourceGroup, vnetName, subnetName)
6✔
981
}
982

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

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

4✔
1014
        // stores the mount options already included in mountOptions
4✔
1015
        included := make(map[string]bool)
4✔
1016

4✔
1017
        for _, mountOption := range mountOptions {
11✔
1018
                for k := range defaultMountOptions {
49✔
1019
                        if strings.HasPrefix(mountOption, k) {
46✔
1020
                                included[k] = true
4✔
1021
                        }
4✔
1022
                }
1023
        }
1024

1025
        allMountOptions := mountOptions
4✔
1026

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

1037
        return allMountOptions
4✔
1038
}
1039

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

1058
func createStorageAccountSecret(account, key string) map[string]string {
1✔
1059
        secret := make(map[string]string)
1✔
1060
        secret[defaultSecretAccountName] = account
1✔
1061
        secret[defaultSecretAccountKey] = key
1✔
1062
        return secret
1✔
1063
}
1✔
1064

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

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

1094
// replaceWithMap replace key with value for str
1095
func replaceWithMap(str string, m map[string]string) string {
14✔
1096
        for k, v := range m {
19✔
1097
                if k != "" {
9✔
1098
                        str = strings.ReplaceAll(str, k, v)
4✔
1099
                }
4✔
1100
        }
1101
        return str
14✔
1102
}
1103

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

1116
func isReadOnlyFromCapability(vc *csi.VolumeCapability) bool {
7✔
1117
        if vc.GetAccessMode() == nil {
9✔
1118
                return false
2✔
1119
        }
2✔
1120
        mode := vc.GetAccessMode().GetMode()
5✔
1121
        return (mode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY ||
5✔
1122
                mode == csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY)
5✔
1123
}
1124

1125
// generateVolumeName returns a PV name with clusterName prefix. The function
1126
// should be used to generate a name of GCE PD or Cinder volume. It basically
1127
// adds "<clusterName>-dynamic-" before the PV name, making sure the resulting
1128
// string fits given length and cuts "dynamic" if not.
1129
func generateVolumeName(clusterName, pvName string, maxLength int) string {
3✔
1130
        prefix := clusterName + "-dynamic"
3✔
1131
        pvLen := len(pvName)
3✔
1132
        // cut the "<clusterName>-dynamic" to fit full pvName into maxLength
3✔
1133
        // +1 for the '-' dash
3✔
1134
        if pvLen+1+len(prefix) > maxLength {
5✔
1135
                prefix = prefix[:maxLength-pvLen-1]
2✔
1136
        }
2✔
1137
        return prefix + "-" + pvName
3✔
1138
}
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