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

kubernetes-sigs / blob-csi-driver / 6498619488

12 Oct 2023 04:54PM CUT coverage: 80.606%. Remained the same
6498619488

Pull #1044

github

web-flow
chore(deps): bump github.com/onsi/ginkgo/v2 from 2.11.0 to 2.13.0

Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.11.0 to 2.13.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.11.0...v2.13.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1044: chore(deps): bump github.com/onsi/ginkgo/v2 from 2.11.0 to 2.13.0

1808 of 2243 relevant lines covered (80.61%)

5.68 hits per line

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

86.09
/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
        "fmt"
21
        "os"
22
        "strconv"
23
        "strings"
24
        "sync"
25
        "time"
26

27
        "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-09-01/storage"
28
        azstorage "github.com/Azure/azure-sdk-for-go/storage"
29
        az "github.com/Azure/go-autorest/autorest/azure"
30
        "github.com/container-storage-interface/spec/lib/go/csi"
31
        "github.com/pborman/uuid"
32
        "golang.org/x/net/context"
33

34
        v1 "k8s.io/api/core/v1"
35
        "k8s.io/apimachinery/pkg/api/errors"
36
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37
        "k8s.io/client-go/kubernetes"
38
        "k8s.io/klog/v2"
39
        k8sutil "k8s.io/kubernetes/pkg/volume/util"
40
        mount "k8s.io/mount-utils"
41
        utilexec "k8s.io/utils/exec"
42

43
        csicommon "sigs.k8s.io/blob-csi-driver/pkg/csi-common"
44
        "sigs.k8s.io/blob-csi-driver/pkg/util"
45
        azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
46
        azure "sigs.k8s.io/cloud-provider-azure/pkg/provider"
47
)
48

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

116
        // See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
117
        containerNameMinLength = 3
118
        containerNameMaxLength = 63
119

120
        accountNotProvisioned                   = "StorageAccountIsNotProvisioned"
121
        tooManyRequests                         = "TooManyRequests"
122
        clientThrottled                         = "client throttled"
123
        containerBeingDeletedDataplaneAPIError  = "ContainerBeingDeleted"
124
        containerBeingDeletedManagementAPIError = "container is being deleted"
125
        statusCodeNotFound                      = "StatusCode=404"
126
        httpCodeNotFound                        = "HTTPStatusCode: 404"
127

128
        // 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
129
        containerMaxSize = 100 * util.TiB
130

131
        subnetTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s"
132

133
        defaultNamespace = "default"
134

135
        pvcNameKey           = "csi.storage.k8s.io/pvc/name"
136
        pvcNamespaceKey      = "csi.storage.k8s.io/pvc/namespace"
137
        pvNameKey            = "csi.storage.k8s.io/pv/name"
138
        pvcNameMetadata      = "${pvc.metadata.name}"
139
        pvcNamespaceMetadata = "${pvc.metadata.namespace}"
140
        pvNameMetadata       = "${pv.metadata.name}"
141

142
        VolumeID = "volumeid"
143

144
        defaultStorageEndPointSuffix = "core.windows.net"
145
)
146

147
var (
148
        supportedProtocolList = []string{Fuse, Fuse2, NFS}
149
        retriableErrors       = []string{accountNotProvisioned, tooManyRequests, statusCodeNotFound, containerBeingDeletedDataplaneAPIError, containerBeingDeletedManagementAPIError, clientThrottled}
150
)
151

152
// DriverOptions defines driver parameters specified in driver deployment
153
type DriverOptions struct {
154
        NodeID                                 string
155
        DriverName                             string
156
        CloudConfigSecretName                  string
157
        CloudConfigSecretNamespace             string
158
        CustomUserAgent                        string
159
        UserAgentSuffix                        string
160
        BlobfuseProxyEndpoint                  string
161
        EnableBlobfuseProxy                    bool
162
        BlobfuseProxyConnTimout                int
163
        EnableBlobMockMount                    bool
164
        AllowEmptyCloudConfig                  bool
165
        AllowInlineVolumeKeyAccessWithIdentity bool
166
        EnableGetVolumeStats                   bool
167
        AppendTimeStampInCacheDir              bool
168
        AppendMountErrorHelpLink               bool
169
        MountPermissions                       uint64
170
        KubeAPIQPS                             float64
171
        KubeAPIBurst                           int
172
        EnableAznfsMount                       bool
173
        VolStatsCacheExpireInMinutes           int
174
}
175

176
// Driver implements all interfaces of CSI drivers
177
type Driver struct {
178
        csicommon.CSIDriver
179

180
        cloud                      *azure.Cloud
181
        cloudConfigSecretName      string
182
        cloudConfigSecretNamespace string
183
        customUserAgent            string
184
        userAgentSuffix            string
185
        blobfuseProxyEndpoint      string
186
        // enableBlobMockMount is only for testing, DO NOT set as true in non-testing scenario
187
        enableBlobMockMount                    bool
188
        enableBlobfuseProxy                    bool
189
        allowEmptyCloudConfig                  bool
190
        enableGetVolumeStats                   bool
191
        allowInlineVolumeKeyAccessWithIdentity bool
192
        appendTimeStampInCacheDir              bool
193
        appendMountErrorHelpLink               bool
194
        blobfuseProxyConnTimout                int
195
        mountPermissions                       uint64
196
        kubeAPIQPS                             float64
197
        kubeAPIBurst                           int
198
        enableAznfsMount                       bool
199
        mounter                                *mount.SafeFormatAndMount
200
        volLockMap                             *util.LockMap
201
        // A map storing all volumes with ongoing operations so that additional operations
202
        // for that same volume (as defined by VolumeID) return an Aborted error
203
        volumeLocks *volumeLocks
204
        // only for nfs feature
205
        subnetLockMap *util.LockMap
206
        // a map storing all volumes created by this driver <volumeName, accountName>
207
        volMap sync.Map
208
        // a timed cache storing all volumeIDs and storage accounts that are using data plane API
209
        dataPlaneAPIVolCache azcache.Resource
210
        // a timed cache storing account search history (solve account list throttling issue)
211
        accountSearchCache azcache.Resource
212
        // a timed cache storing volume stats <volumeID, volumeStats>
213
        volStatsCache azcache.Resource
214
}
215

216
// NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version &
217
// does not support optional driver plugin info manifest field. Refer to CSI spec for more details.
218
func NewDriver(options *DriverOptions) *Driver {
108✔
219
        d := Driver{
108✔
220
                volLockMap:                             util.NewLockMap(),
108✔
221
                subnetLockMap:                          util.NewLockMap(),
108✔
222
                volumeLocks:                            newVolumeLocks(),
108✔
223
                cloudConfigSecretName:                  options.CloudConfigSecretName,
108✔
224
                cloudConfigSecretNamespace:             options.CloudConfigSecretNamespace,
108✔
225
                customUserAgent:                        options.CustomUserAgent,
108✔
226
                userAgentSuffix:                        options.UserAgentSuffix,
108✔
227
                blobfuseProxyEndpoint:                  options.BlobfuseProxyEndpoint,
108✔
228
                enableBlobfuseProxy:                    options.EnableBlobfuseProxy,
108✔
229
                allowInlineVolumeKeyAccessWithIdentity: options.AllowInlineVolumeKeyAccessWithIdentity,
108✔
230
                blobfuseProxyConnTimout:                options.BlobfuseProxyConnTimout,
108✔
231
                enableBlobMockMount:                    options.EnableBlobMockMount,
108✔
232
                allowEmptyCloudConfig:                  options.AllowEmptyCloudConfig,
108✔
233
                enableGetVolumeStats:                   options.EnableGetVolumeStats,
108✔
234
                appendMountErrorHelpLink:               options.AppendMountErrorHelpLink,
108✔
235
                mountPermissions:                       options.MountPermissions,
108✔
236
                kubeAPIQPS:                             options.KubeAPIQPS,
108✔
237
                kubeAPIBurst:                           options.KubeAPIBurst,
108✔
238
                enableAznfsMount:                       options.EnableAznfsMount,
108✔
239
        }
108✔
240
        d.Name = options.DriverName
108✔
241
        d.Version = driverVersion
108✔
242
        d.NodeID = options.NodeID
108✔
243

108✔
244
        var err error
108✔
245
        getter := func(key string) (interface{}, error) { return nil, nil }
113✔
246
        if d.accountSearchCache, err = azcache.NewTimedCache(time.Minute, getter, false); err != nil {
108✔
247
                klog.Fatalf("%v", err)
×
248
        }
×
249
        if d.dataPlaneAPIVolCache, err = azcache.NewTimedCache(10*time.Minute, getter, false); err != nil {
108✔
250
                klog.Fatalf("%v", err)
×
251
        }
×
252

253
        if options.VolStatsCacheExpireInMinutes <= 0 {
216✔
254
                options.VolStatsCacheExpireInMinutes = 10 // default expire in 10 minutes
108✔
255
        }
108✔
256
        if d.volStatsCache, err = azcache.NewTimedCache(time.Duration(options.VolStatsCacheExpireInMinutes)*time.Minute, getter, false); err != nil {
108✔
257
                klog.Fatalf("%v", err)
×
258
        }
×
259
        return &d
108✔
260
}
261

262
// Run driver initialization
263
func (d *Driver) Run(endpoint, kubeconfig string, testBool bool) {
2✔
264
        versionMeta, err := GetVersionYAML(d.Name)
2✔
265
        if err != nil {
2✔
266
                klog.Fatalf("%v", err)
×
267
        }
×
268
        klog.Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta)
2✔
269

2✔
270
        userAgent := GetUserAgent(d.Name, d.customUserAgent, d.userAgentSuffix)
2✔
271
        klog.V(2).Infof("driver userAgent: %s", userAgent)
2✔
272
        d.cloud, err = getCloudProvider(kubeconfig, d.NodeID, d.cloudConfigSecretName, d.cloudConfigSecretNamespace, userAgent, d.allowEmptyCloudConfig, d.kubeAPIQPS, d.kubeAPIBurst)
2✔
273
        if err != nil {
2✔
274
                klog.Fatalf("failed to get Azure Cloud Provider, error: %v", err)
×
275
        }
×
276
        klog.V(2).Infof("cloud: %s, location: %s, rg: %s, VnetName: %s, VnetResourceGroup: %s, SubnetName: %s", d.cloud.Cloud, d.cloud.Location, d.cloud.ResourceGroup, d.cloud.VnetName, d.cloud.VnetResourceGroup, d.cloud.SubnetName)
2✔
277

2✔
278
        d.mounter = &mount.SafeFormatAndMount{
2✔
279
                Interface: mount.New(""),
2✔
280
                Exec:      utilexec.New(),
2✔
281
        }
2✔
282

2✔
283
        // Initialize default library driver
2✔
284
        d.AddControllerServiceCapabilities(
2✔
285
                []csi.ControllerServiceCapability_RPC_Type{
2✔
286
                        csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
2✔
287
                        //csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
2✔
288
                        //csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
2✔
289
                        csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
2✔
290
                        csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
2✔
291
                })
2✔
292
        d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{
2✔
293
                csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
2✔
294
                csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
2✔
295
                csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
2✔
296
                csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
2✔
297
                csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
2✔
298
                csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER,
2✔
299
                csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
2✔
300
        })
2✔
301

2✔
302
        nodeCap := []csi.NodeServiceCapability_RPC_Type{
2✔
303
                csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
2✔
304
                csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
2✔
305
        }
2✔
306
        if d.enableGetVolumeStats {
2✔
307
                nodeCap = append(nodeCap, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
×
308
        }
×
309
        d.AddNodeServiceCapabilities(nodeCap)
2✔
310

2✔
311
        s := csicommon.NewNonBlockingGRPCServer()
2✔
312
        // Driver d act as IdentityServer, ControllerServer and NodeServer
2✔
313
        s.Start(endpoint, d, d, d, testBool)
2✔
314
        s.Wait()
2✔
315
}
316

317
// GetContainerInfo get container info according to volume id
318
// the format of VolumeId is: rg#accountName#containerName#uuid#secretNamespace#subsID
319
//
320
// e.g.
321
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#"
322
// output: rg, f5713de20cde511e8ba4900, containerName, "" , ""
323
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#"
324
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, ""
325
// input: "rg#f5713de20cde511e8ba4900#containerName#uuid#namespace#subsID"
326
// output: rg, f5713de20cde511e8ba4900, containerName, namespace, subsID
327
func GetContainerInfo(id string) (string, string, string, string, string, error) {
33✔
328
        segments := strings.Split(id, separator)
33✔
329
        if len(segments) < 3 {
40✔
330
                return "", "", "", "", "", fmt.Errorf("error parsing volume id: %q, should at least contain two #", id)
7✔
331
        }
7✔
332
        var secretNamespace, subsID string
26✔
333
        if len(segments) > 4 {
32✔
334
                secretNamespace = segments[4]
6✔
335
        }
6✔
336
        if len(segments) > 5 {
29✔
337
                subsID = segments[5]
3✔
338
        }
3✔
339
        return segments[0], segments[1], segments[2], secretNamespace, subsID, nil
26✔
340
}
341

342
// A container name must be a valid DNS name, conforming to the following naming rules:
343
//  1. Container names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
344
//  2. Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names.
345
//  3. All letters in a container name must be lowercase.
346
//  4. Container names must be from 3 through 63 characters long.
347
//
348
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
349
func getValidContainerName(volumeName, protocol string) string {
5✔
350
        containerName := strings.ToLower(volumeName)
5✔
351
        if len(containerName) > containerNameMaxLength {
6✔
352
                containerName = containerName[0:containerNameMaxLength]
1✔
353
        }
1✔
354
        if !checkContainerNameBeginAndEnd(containerName) || len(containerName) < containerNameMinLength {
5✔
355
                // now we set as 63 for maximum container name length
×
356
                // todo: get cluster name
×
357
                containerName = k8sutil.GenerateVolumeName(fmt.Sprintf("pvc-%s", protocol), uuid.NewUUID().String(), 63)
×
358
                klog.Warningf("requested volume name (%s) is invalid, regenerated as (%q)", volumeName, containerName)
×
359
        }
×
360
        return strings.Replace(containerName, "--", "-", -1)
5✔
361
}
362

363
func checkContainerNameBeginAndEnd(containerName string) bool {
11✔
364
        length := len(containerName)
11✔
365
        if (('a' <= containerName[0] && containerName[0] <= 'z') ||
11✔
366
                ('0' <= containerName[0] && containerName[0] <= '9')) &&
11✔
367
                (('a' <= containerName[length-1] && containerName[length-1] <= 'z') ||
11✔
368
                        ('0' <= containerName[length-1] && containerName[length-1] <= '9')) {
20✔
369
                return true
9✔
370
        }
9✔
371

372
        return false
2✔
373
}
374

375
// isSASToken checks if the key contains the patterns.
376
// SAS token format could refer to https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
377
func isSASToken(key string) bool {
3✔
378
        return strings.HasPrefix(key, "?")
3✔
379
}
3✔
380

381
// GetAuthEnv return <accountName, containerName, authEnv, error>
382
func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attrib, secrets map[string]string) (string, string, string, string, []string, error) {
10✔
383
        rgName, accountName, containerName, secretNamespace, _, err := GetContainerInfo(volumeID)
10✔
384
        if err != nil {
12✔
385
                // ignore volumeID parsing error
2✔
386
                klog.V(2).Infof("parsing volumeID(%s) return with error: %v", volumeID, err)
2✔
387
                err = nil
2✔
388
        }
2✔
389

390
        var (
10✔
391
                subsID                  string
10✔
392
                accountKey              string
10✔
393
                accountSasToken         string
10✔
394
                msiSecret               string
10✔
395
                storageSPNClientSecret  string
10✔
396
                storageSPNClientID      string
10✔
397
                storageSPNTenantID      string
10✔
398
                secretName              string
10✔
399
                pvcNamespace            string
10✔
400
                keyVaultURL             string
10✔
401
                keyVaultSecretName      string
10✔
402
                keyVaultSecretVersion   string
10✔
403
                azureStorageAuthType    string
10✔
404
                authEnv                 []string
10✔
405
                getAccountKeyFromSecret bool
10✔
406
                getLatestAccountKey     bool
10✔
407
        )
10✔
408

10✔
409
        for k, v := range attrib {
36✔
410
                switch strings.ToLower(k) {
26✔
411
                case subscriptionIDField:
1✔
412
                        subsID = v
1✔
413
                case resourceGroupField:
×
414
                        rgName = v
×
415
                case containerNameField:
3✔
416
                        containerName = v
3✔
417
                case keyVaultURLField:
1✔
418
                        keyVaultURL = v
1✔
419
                case keyVaultSecretNameField:
1✔
420
                        keyVaultSecretName = v
1✔
421
                case keyVaultSecretVersionField:
1✔
422
                        keyVaultSecretVersion = v
1✔
423
                case storageAccountField:
2✔
424
                        accountName = v
2✔
425
                case storageAccountNameField: // for compatibility
1✔
426
                        accountName = v
1✔
427
                case secretNameField:
1✔
428
                        secretName = v
1✔
429
                case secretNamespaceField:
1✔
430
                        secretNamespace = v
1✔
431
                case pvcNamespaceKey:
1✔
432
                        pvcNamespace = v
1✔
433
                case getAccountKeyFromSecretField:
1✔
434
                        getAccountKeyFromSecret = strings.EqualFold(v, trueValue)
1✔
435
                case storageAuthTypeField:
×
436
                        azureStorageAuthType = v
×
437
                        authEnv = append(authEnv, "AZURE_STORAGE_AUTH_TYPE="+v)
×
438
                case storageIentityClientIDField:
1✔
439
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_CLIENT_ID="+v)
1✔
440
                case storageIdentityObjectIDField:
1✔
441
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_OBJECT_ID="+v)
1✔
442
                case storageIdentityResourceIDField:
1✔
443
                        authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_RESOURCE_ID="+v)
1✔
444
                case msiEndpointField:
1✔
445
                        authEnv = append(authEnv, "MSI_ENDPOINT="+v)
1✔
446
                case storageSPNClientIDField:
1✔
447
                        storageSPNClientID = v
1✔
448
                case storageSPNTenantIDField:
1✔
449
                        storageSPNTenantID = v
1✔
450
                case storageAADEndpointField:
1✔
451
                        authEnv = append(authEnv, "AZURE_STORAGE_AAD_ENDPOINT="+v)
1✔
452
                case getLatestAccountKeyField:
1✔
453
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
454
                                return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
455
                        }
1✔
456
                }
457
        }
458
        klog.V(2).Infof("volumeID(%s) authEnv: %s", volumeID, authEnv)
9✔
459

9✔
460
        if protocol == NFS {
11✔
461
                // nfs protocol does not need account key, return directly
2✔
462
                return rgName, accountName, accountKey, containerName, authEnv, err
2✔
463
        }
2✔
464

465
        if secretNamespace == "" {
13✔
466
                if pvcNamespace == "" {
12✔
467
                        secretNamespace = defaultNamespace
6✔
468
                } else {
6✔
469
                        secretNamespace = pvcNamespace
×
470
                }
×
471
        }
472

473
        if rgName == "" {
8✔
474
                rgName = d.cloud.ResourceGroup
1✔
475
        }
1✔
476

477
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
478
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
479
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
480
        if keyVaultURL != "" {
8✔
481
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
1✔
482
                if err != nil {
2✔
483
                        return rgName, accountName, accountKey, containerName, authEnv, err
1✔
484
                }
1✔
485
                if isSASToken(key) {
×
486
                        accountSasToken = key
×
487
                } else {
×
488
                        accountKey = key
×
489
                }
×
490
        } else {
6✔
491
                if len(secrets) == 0 {
11✔
492
                        if secretName == "" && accountName != "" {
9✔
493
                                secretName = fmt.Sprintf(secretNameTemplate, accountName)
4✔
494
                        }
4✔
495
                        if secretName != "" {
10✔
496
                                // read from k8s secret first
5✔
497
                                var name, spnClientID, spnTenantID string
5✔
498
                                name, accountKey, accountSasToken, msiSecret, storageSPNClientSecret, spnClientID, spnTenantID, err = d.GetInfoFromSecret(ctx, secretName, secretNamespace)
5✔
499
                                if name != "" {
5✔
500
                                        accountName = name
×
501
                                }
×
502
                                if spnClientID != "" {
5✔
503
                                        storageSPNClientID = spnClientID
×
504
                                }
×
505
                                if spnTenantID != "" {
5✔
506
                                        storageSPNTenantID = spnTenantID
×
507
                                }
×
508
                                if err != nil && strings.EqualFold(azureStorageAuthType, "msi") {
5✔
509
                                        klog.V(2).Infof("ignore error(%v) since secret is optional for auth type(%s)", err, azureStorageAuthType)
×
510
                                        err = nil
×
511
                                }
×
512
                                if err != nil && !getAccountKeyFromSecret && (azureStorageAuthType == "" || strings.EqualFold(azureStorageAuthType, "key")) {
10✔
513
                                        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✔
514
                                                accountName, secretNamespace, secretName, err)
5✔
515
                                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
5✔
516
                                        if err != nil {
7✔
517
                                                return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
2✔
518
                                        }
2✔
519
                                }
520
                        }
521
                } else {
1✔
522
                        for k, v := range secrets {
8✔
523
                                v = strings.TrimSpace(v)
7✔
524
                                switch strings.ToLower(k) {
7✔
525
                                case accountNameField:
1✔
526
                                        accountName = v
1✔
527
                                case defaultSecretAccountName: // for compatibility with built-in blobfuse plugin
1✔
528
                                        accountName = v
1✔
529
                                case accountKeyField:
1✔
530
                                        accountKey = v
1✔
531
                                case defaultSecretAccountKey: // for compatibility with built-in blobfuse plugin
1✔
532
                                        accountKey = v
1✔
533
                                case accountSasTokenField:
1✔
534
                                        accountSasToken = v
1✔
535
                                case msiSecretField:
1✔
536
                                        msiSecret = v
1✔
537
                                case storageSPNClientSecretField:
1✔
538
                                        storageSPNClientSecret = v
1✔
539
                                case storageSPNClientIDField:
×
540
                                        storageSPNClientID = v
×
541
                                case storageSPNTenantIDField:
×
542
                                        storageSPNTenantID = v
×
543
                                }
544
                        }
545
                }
546
        }
547

548
        if containerName == "" {
4✔
549
                err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
550
        }
×
551

552
        if accountKey != "" {
8✔
553
                authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
4✔
554
        }
4✔
555

556
        if accountSasToken != "" {
5✔
557
                klog.V(2).Infof("accountSasToken is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
558
                authEnv = append(authEnv, "AZURE_STORAGE_SAS_TOKEN="+accountSasToken)
1✔
559
        }
1✔
560

561
        if msiSecret != "" {
5✔
562
                klog.V(2).Infof("msiSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
563
                authEnv = append(authEnv, "MSI_SECRET="+msiSecret)
1✔
564
        }
1✔
565

566
        if storageSPNClientSecret != "" {
5✔
567
                klog.V(2).Infof("storageSPNClientSecret is not empty, use it to access storage account(%s), container(%s)", accountName, containerName)
1✔
568
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+storageSPNClientSecret)
1✔
569
        }
1✔
570

571
        if storageSPNClientID != "" {
4✔
572
                klog.V(2).Infof("storageSPNClientID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNClientID, accountName, containerName)
×
573
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+storageSPNClientID)
×
574
        }
×
575

576
        if storageSPNTenantID != "" {
4✔
577
                klog.V(2).Infof("storageSPNTenantID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNTenantID, accountName, containerName)
×
578
                authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+storageSPNTenantID)
×
579
        }
×
580

581
        return rgName, accountName, accountKey, containerName, authEnv, err
4✔
582
}
583

584
// GetStorageAccountAndContainer get storage account and container info
585
// returns <accountName, accountKey, accountSasToken, containerName>
586
// only for e2e testing
587
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
3✔
588
        var (
3✔
589
                subsID                string
3✔
590
                accountName           string
3✔
591
                accountKey            string
3✔
592
                accountSasToken       string
3✔
593
                containerName         string
3✔
594
                keyVaultURL           string
3✔
595
                keyVaultSecretName    string
3✔
596
                keyVaultSecretVersion string
3✔
597
                getLatestAccountKey   bool
3✔
598
                err                   error
3✔
599
        )
3✔
600

3✔
601
        for k, v := range attrib {
8✔
602
                switch strings.ToLower(k) {
5✔
603
                case subscriptionIDField:
×
604
                        subsID = v
×
605
                case containerNameField:
1✔
606
                        containerName = v
1✔
607
                case keyVaultURLField:
×
608
                        keyVaultURL = v
×
609
                case keyVaultSecretNameField:
1✔
610
                        keyVaultSecretName = v
1✔
611
                case keyVaultSecretVersionField:
1✔
612
                        keyVaultSecretVersion = v
1✔
613
                case storageAccountField:
×
614
                        accountName = v
×
615
                case storageAccountNameField: // for compatibility
1✔
616
                        accountName = v
1✔
617
                case getLatestAccountKeyField:
1✔
618
                        if getLatestAccountKey, err = strconv.ParseBool(v); err != nil {
2✔
619
                                return "", "", "", "", fmt.Errorf("invalid %s: %s in volume context", getLatestAccountKeyField, v)
1✔
620
                        }
1✔
621
                }
622
        }
623

624
        // 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
625
        // 2. Then if secrets map is not nil, use the key stored in the secrets map.
626
        // 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
627
        if keyVaultURL != "" {
2✔
628
                key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
×
629
                if err != nil {
×
630
                        return "", "", "", "", err
×
631
                }
×
632
                if isSASToken(key) {
×
633
                        accountSasToken = key
×
634
                } else {
×
635
                        accountKey = key
×
636
                }
×
637
        } else {
2✔
638
                if len(secrets) == 0 {
4✔
639
                        var rgName string
2✔
640
                        rgName, accountName, containerName, _, _, err = GetContainerInfo(volumeID)
2✔
641
                        if err != nil {
2✔
642
                                return "", "", "", "", err
×
643
                        }
×
644

645
                        if rgName == "" {
2✔
646
                                rgName = d.cloud.ResourceGroup
×
647
                        }
×
648

649
                        accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName, getLatestAccountKey)
2✔
650
                        if err != nil {
3✔
651
                                return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
1✔
652
                        }
1✔
653
                }
654
        }
655

656
        if containerName == "" {
1✔
657
                return "", "", "", "", fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
×
658
        }
×
659

660
        return accountName, accountKey, accountSasToken, containerName, nil
1✔
661
}
662

663
func IsCorruptedDir(dir string) bool {
4✔
664
        _, pathErr := mount.PathExists(dir)
4✔
665
        return pathErr != nil && mount.IsCorruptedMnt(pathErr)
4✔
666
}
4✔
667

668
func isRetriableError(err error) bool {
5✔
669
        if err != nil {
9✔
670
                for _, v := range retriableErrors {
19✔
671
                        if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(v)) {
18✔
672
                                return true
3✔
673
                        }
3✔
674
                }
675
        }
676
        return false
2✔
677
}
678

679
func isSupportedProtocol(protocol string) bool {
15✔
680
        if protocol == "" {
16✔
681
                return true
1✔
682
        }
1✔
683
        for _, v := range supportedProtocolList {
36✔
684
                if protocol == v {
34✔
685
                        return true
12✔
686
                }
12✔
687
        }
688
        return false
2✔
689
}
690

691
func isSupportedAccessTier(accessTier string) bool {
18✔
692
        if accessTier == "" {
29✔
693
                return true
11✔
694
        }
11✔
695
        for _, tier := range storage.PossibleAccessTierValues() {
25✔
696
                if accessTier == string(tier) {
21✔
697
                        return true
3✔
698
                }
3✔
699
        }
700
        return false
4✔
701
}
702

703
// container names can contain only lowercase letters, numbers, and hyphens,
704
// and must begin and end with a letter or a number
705
func isSupportedContainerNamePrefix(prefix string) bool {
17✔
706
        if prefix == "" {
26✔
707
                return true
9✔
708
        }
9✔
709
        if len(prefix) > 20 {
9✔
710
                return false
1✔
711
        }
1✔
712
        if prefix[0] == '-' {
8✔
713
                return false
1✔
714
        }
1✔
715
        for _, v := range prefix {
19✔
716
                if v != '-' && (v < '0' || v > '9') && (v < 'a' || v > 'z') {
17✔
717
                        return false
4✔
718
                }
4✔
719
        }
720
        return true
2✔
721
}
722

723
// get storage account from secrets map
724
func getStorageAccount(secrets map[string]string) (string, string, error) {
18✔
725
        if secrets == nil {
19✔
726
                return "", "", fmt.Errorf("unexpected: getStorageAccount secrets is nil")
1✔
727
        }
1✔
728

729
        var accountName, accountKey string
17✔
730
        for k, v := range secrets {
53✔
731
                v = strings.TrimSpace(v)
36✔
732
                switch strings.ToLower(k) {
36✔
733
                case accountNameField:
7✔
734
                        accountName = v
7✔
735
                case defaultSecretAccountName: // for compatibility with built-in azurefile plugin
9✔
736
                        accountName = v
9✔
737
                case accountKeyField:
7✔
738
                        accountKey = v
7✔
739
                case defaultSecretAccountKey: // for compatibility with built-in azurefile plugin
9✔
740
                        accountKey = v
9✔
741
                }
742
        }
743

744
        if accountName == "" {
21✔
745
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountNameField, defaultSecretAccountName)
4✔
746
        }
4✔
747
        if accountKey == "" {
16✔
748
                return accountName, accountKey, fmt.Errorf("could not find %s or %s field in secrets", accountKeyField, defaultSecretAccountKey)
3✔
749
        }
3✔
750

751
        accountName = strings.TrimSpace(accountName)
10✔
752
        klog.V(4).Infof("got storage account(%s) from secret", accountName)
10✔
753
        return accountName, accountKey, nil
10✔
754
}
755

756
func getContainerReference(containerName string, secrets map[string]string, env az.Environment) (*azstorage.Container, error) {
9✔
757
        accountName, accountKey, rerr := getStorageAccount(secrets)
9✔
758
        if rerr != nil {
11✔
759
                return nil, rerr
2✔
760
        }
2✔
761
        client, err := azstorage.NewBasicClientOnSovereignCloud(accountName, accountKey, env)
7✔
762
        if err != nil {
13✔
763
                return nil, err
6✔
764
        }
6✔
765
        blobClient := client.GetBlobService()
1✔
766
        container := blobClient.GetContainerReference(containerName)
1✔
767
        if container == nil {
1✔
768
                return nil, fmt.Errorf("ContainerReference of %s is nil", containerName)
×
769
        }
×
770
        return container, nil
1✔
771
}
772

773
func setAzureCredentials(ctx context.Context, kubeClient kubernetes.Interface, accountName, accountKey, secretNamespace string) (string, error) {
6✔
774
        if kubeClient == nil {
8✔
775
                klog.Warningf("could not create secret: kubeClient is nil")
2✔
776
                return "", nil
2✔
777
        }
2✔
778
        if accountName == "" || accountKey == "" {
6✔
779
                return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey)
2✔
780
        }
2✔
781
        secretName := fmt.Sprintf(secretNameTemplate, accountName)
2✔
782
        secret := &v1.Secret{
2✔
783
                ObjectMeta: metav1.ObjectMeta{
2✔
784
                        Namespace: secretNamespace,
2✔
785
                        Name:      secretName,
2✔
786
                },
2✔
787
                Data: map[string][]byte{
2✔
788
                        defaultSecretAccountName: []byte(accountName),
2✔
789
                        defaultSecretAccountKey:  []byte(accountKey),
2✔
790
                },
2✔
791
                Type: "Opaque",
2✔
792
        }
2✔
793
        _, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(ctx, secret, metav1.CreateOptions{})
2✔
794
        if errors.IsAlreadyExists(err) {
3✔
795
                err = nil
1✔
796
        }
1✔
797
        if err != nil {
2✔
798
                return "", fmt.Errorf("couldn't create secret %w", err)
×
799
        }
×
800
        return secretName, err
2✔
801
}
802

803
// GetStorageAccesskey get Azure storage account key from
804
//  1. secrets (if not empty)
805
//  2. use k8s client identity to read from k8s secret
806
//  3. use cluster identity to get from storage account directly
807
func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure.AccountOptions, secrets map[string]string, secretName, secretNamespace string) (string, string, error) {
7✔
808
        if len(secrets) > 0 {
8✔
809
                return getStorageAccount(secrets)
1✔
810
        }
1✔
811

812
        // read from k8s secret first
813
        if secretName == "" {
10✔
814
                secretName = fmt.Sprintf(secretNameTemplate, accountOptions.Name)
4✔
815
        }
4✔
816
        _, accountKey, _, _, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint
6✔
817
        if err != nil {
10✔
818
                klog.V(2).Infof("could not get account(%s) key from secret(%s) namespace(%s), error: %v, use cluster identity to get account key instead", accountOptions.Name, secretName, secretNamespace, err)
4✔
819
                accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup, accountOptions.GetLatestAccountKey)
4✔
820
        }
4✔
821
        return accountOptions.Name, accountKey, err
6✔
822
}
823

824
// GetInfoFromSecret get info from k8s secret
825
// return <accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, error>
826
func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, string, string, error) {
15✔
827
        if d.cloud.KubeClient == nil {
24✔
828
                return "", "", "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName)
9✔
829
        }
9✔
830

831
        secret, err := d.cloud.KubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
6✔
832
        if err != nil {
8✔
833
                return "", "", "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err)
2✔
834
        }
2✔
835

836
        accountName := strings.TrimSpace(string(secret.Data[defaultSecretAccountName][:]))
4✔
837
        accountKey := strings.TrimSpace(string(secret.Data[defaultSecretAccountKey][:]))
4✔
838
        accountSasToken := strings.TrimSpace(string(secret.Data[accountSasTokenField][:]))
4✔
839
        msiSecret := strings.TrimSpace(string(secret.Data[msiSecretField][:]))
4✔
840
        spnClientSecret := strings.TrimSpace(string(secret.Data[storageSPNClientSecretField][:]))
4✔
841
        spnClientID := strings.TrimSpace(string(secret.Data[storageSPNClientIDField][:]))
4✔
842
        spnTenantID := strings.TrimSpace(string(secret.Data[storageSPNTenantIDField][:]))
4✔
843

4✔
844
        klog.V(4).Infof("got storage account(%s) from secret(%s) namespace(%s)", accountName, secretName, secretNamespace)
4✔
845
        return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil
4✔
846
}
847

848
// getSubnetResourceID get default subnet resource ID from cloud provider config
849
func (d *Driver) getSubnetResourceID(vnetResourceGroup, vnetName, subnetName string) string {
6✔
850
        subsID := d.cloud.SubscriptionID
6✔
851
        if len(d.cloud.NetworkResourceSubscriptionID) > 0 {
10✔
852
                subsID = d.cloud.NetworkResourceSubscriptionID
4✔
853
        }
4✔
854

855
        if len(vnetResourceGroup) == 0 {
11✔
856
                vnetResourceGroup = d.cloud.ResourceGroup
5✔
857
                if len(d.cloud.VnetResourceGroup) > 0 {
8✔
858
                        vnetResourceGroup = d.cloud.VnetResourceGroup
3✔
859
                }
3✔
860
        }
861

862
        if len(vnetName) == 0 {
11✔
863
                vnetName = d.cloud.VnetName
5✔
864
        }
5✔
865

866
        if len(subnetName) == 0 {
11✔
867
                subnetName = d.cloud.SubnetName
5✔
868
        }
5✔
869
        return fmt.Sprintf(subnetTemplate, subsID, vnetResourceGroup, vnetName, subnetName)
6✔
870
}
871

872
func (d *Driver) useDataPlaneAPI(volumeID, accountName string) bool {
4✔
873
        cache, err := d.dataPlaneAPIVolCache.Get(volumeID, azcache.CacheReadTypeDefault)
4✔
874
        if err != nil {
4✔
875
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", volumeID, err)
×
876
        }
×
877
        if cache != nil {
7✔
878
                return true
3✔
879
        }
3✔
880
        cache, err = d.dataPlaneAPIVolCache.Get(accountName, azcache.CacheReadTypeDefault)
1✔
881
        if err != nil {
1✔
882
                klog.Errorf("get(%s) from dataPlaneAPIVolCache failed with error: %v", accountName, err)
×
883
        }
×
884
        if cache != nil {
1✔
885
                return true
×
886
        }
×
887
        return false
1✔
888
}
889

890
// appendDefaultMountOptions return mount options combined with mountOptions and defaultMountOptions
891
func appendDefaultMountOptions(mountOptions []string, tmpPath, containerName string) []string {
4✔
892
        var defaultMountOptions = map[string]string{
4✔
893
                "--pre-mount-validate": "true",
4✔
894
                "--use-https":          "true",
4✔
895
                "--tmp-path":           tmpPath,
4✔
896
                "--container-name":     containerName,
4✔
897
                // prevent billing charges on mounting
4✔
898
                "--cancel-list-on-mount-seconds": "10",
4✔
899
                // allow remounting using a non-empty tmp-path
4✔
900
                "--empty-dir-check": "false",
4✔
901
        }
4✔
902

4✔
903
        // stores the mount options already included in mountOptions
4✔
904
        included := make(map[string]bool)
4✔
905

4✔
906
        for _, mountOption := range mountOptions {
11✔
907
                for k := range defaultMountOptions {
49✔
908
                        if strings.HasPrefix(mountOption, k) {
46✔
909
                                included[k] = true
4✔
910
                        }
4✔
911
                }
912
        }
913

914
        allMountOptions := mountOptions
4✔
915

4✔
916
        for k, v := range defaultMountOptions {
28✔
917
                if _, isIncluded := included[k]; !isIncluded {
44✔
918
                        if v != "" {
40✔
919
                                allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", k, v))
20✔
920
                        } else {
20✔
921
                                allMountOptions = append(allMountOptions, k)
×
922
                        }
×
923
                }
924
        }
925

926
        return allMountOptions
4✔
927
}
928

929
// chmodIfPermissionMismatch only perform chmod when permission mismatches
930
func chmodIfPermissionMismatch(targetPath string, mode os.FileMode) error {
3✔
931
        info, err := os.Lstat(targetPath)
3✔
932
        if err != nil {
4✔
933
                return err
1✔
934
        }
1✔
935
        perm := info.Mode() & os.ModePerm
2✔
936
        if perm != mode {
3✔
937
                klog.V(2).Infof("chmod targetPath(%s, mode:0%o) with permissions(0%o)", targetPath, info.Mode(), mode)
1✔
938
                if err := os.Chmod(targetPath, mode); err != nil {
1✔
939
                        return err
×
940
                }
×
941
        } else {
1✔
942
                klog.V(2).Infof("skip chmod on targetPath(%s) since mode is already 0%o)", targetPath, info.Mode())
1✔
943
        }
1✔
944
        return nil
2✔
945
}
946

947
func createStorageAccountSecret(account, key string) map[string]string {
1✔
948
        secret := make(map[string]string)
1✔
949
        secret[defaultSecretAccountName] = account
1✔
950
        secret[defaultSecretAccountKey] = key
1✔
951
        return secret
1✔
952
}
1✔
953

954
// setKeyValueInMap set key/value pair in map
955
// key in the map is case insensitive, if key already exists, overwrite existing value
956
func setKeyValueInMap(m map[string]string, key, value string) {
6✔
957
        if m == nil {
7✔
958
                return
1✔
959
        }
1✔
960
        for k := range m {
16✔
961
                if strings.EqualFold(k, key) {
13✔
962
                        m[k] = value
2✔
963
                        return
2✔
964
                }
2✔
965
        }
966
        m[key] = value
3✔
967
}
968

969
// replaceWithMap replace key with value for str
970
func replaceWithMap(str string, m map[string]string) string {
11✔
971
        for k, v := range m {
16✔
972
                if k != "" {
9✔
973
                        str = strings.ReplaceAll(str, k, v)
4✔
974
                }
4✔
975
        }
976
        return str
11✔
977
}
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