Skip to content

Commit 2e22be3

Browse files
authored
Merge pull request #708 from andyzhangx/containername-pvc-metadata
feat: support pv/pvc metadata for containerName parameter
2 parents 970df91 + fdcefe0 commit 2e22be3

File tree

6 files changed

+100
-10
lines changed

6 files changed

+100
-10
lines changed

docs/driver-parameters.md

+11-5
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,21 @@ volumeAttributes.keyVaultURL | Azure Key Vault DNS name | existing Azure Key Vau
8787
volumeAttributes.keyVaultSecretName | Azure Key Vault secret name | existing Azure Key Vault secret name | No |
8888
volumeAttributes.keyVaultSecretVersion | Azure Key Vault secret version | existing version | No |if empty, driver will use "current version"
8989

90-
- Note
91-
- only mounting blobfuse requires account key, and if secret is not provided in PV config, driver would try to get `azure-storage-account-{accountname}-secret` in the pod namespace, if not found, driver would try using kubelet identity to get account key directly using Azure API.
92-
- mounting blob storage NFSv3 does not need account key, it requires storage account configured with same vnet with agent node.
93-
- blobfuse does not support private link well, check details [here](https://github.com/Azure/azure-storage-fuse/wiki/2.-Configuring-and-Running#private-link)
94-
9590
- create a Kubernetes secret for `nodeStageSecretRef.name`
9691
```console
9792
kubectl create secret generic azure-secret --from-literal=azurestorageaccountname="xxx" --from-literal azurestorageaccountkey="xxx" --type=Opaque
9893
kubectl create secret generic azure-secret --from-literal=azurestorageaccountname="xxx" --from-literal azurestorageaccountsastoken="xxx" --type=Opaque
9994
kubectl create secret generic azure-secret --from-literal msisecret="xxx" --type=Opaque
10095
kubectl create secret generic azure-secret --from-literal azurestoragespnclientsecret="xxx" --type=Opaque
10196
```
97+
98+
### Tips
99+
- only mounting blobfuse requires account key, and if secret is not provided in PV config, driver would try to get `azure-storage-account-{accountname}-secret` in the pod namespace, if not found, driver would try using kubelet identity to get account key directly using Azure API.
100+
- mounting blob storage NFSv3 does not need account key, it requires storage account configured with same vnet with agent node.
101+
- blobfuse does not support private link well, check details [here](https://github.com/Azure/azure-storage-fuse/wiki/2.-Configuring-and-Running#private-link)
102+
103+
#### `containerName` parameter supports following pv/pvc metadata transform
104+
> if `containerName` value contains following strings, it would be converted into corresponding pv/pvc name or namespace
105+
- `${pvc.metadata.name}`
106+
- `${pvc.metadata.namespace}`
107+
- `${pv.metadata.name}`

pkg/blob/blob.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,12 @@ const (
110110

111111
defaultNamespace = "default"
112112

113-
pvcNameKey = "csi.storage.k8s.io/pvc/name"
114-
pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace"
115-
pvNameKey = "csi.storage.k8s.io/pv/name"
113+
pvcNameKey = "csi.storage.k8s.io/pvc/name"
114+
pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace"
115+
pvNameKey = "csi.storage.k8s.io/pv/name"
116+
pvcNameMetadata = "${pvc.metadata.name}"
117+
pvcNamespaceMetadata = "${pvc.metadata.namespace}"
118+
pvNameMetadata = "${pv.metadata.name}"
116119

117120
VolumeID = "volumeid"
118121
)
@@ -818,3 +821,13 @@ func setKeyValueInMap(m map[string]string, key, value string) {
818821
}
819822
m[key] = value
820823
}
824+
825+
// replaceWithMap replace key with value for str
826+
func replaceWithMap(str string, m map[string]string) string {
827+
for k, v := range m {
828+
if k != "" {
829+
str = strings.ReplaceAll(str, k, v)
830+
}
831+
}
832+
return str
833+
}

pkg/blob/blob_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -1004,3 +1004,55 @@ func TestSetKeyValueInMap(t *testing.T) {
10041004
}
10051005
}
10061006
}
1007+
1008+
func TestReplaceWithMap(t *testing.T) {
1009+
tests := []struct {
1010+
desc string
1011+
str string
1012+
m map[string]string
1013+
expected string
1014+
}{
1015+
{
1016+
desc: "empty string",
1017+
str: "",
1018+
expected: "",
1019+
},
1020+
{
1021+
desc: "empty map",
1022+
str: "",
1023+
m: map[string]string{},
1024+
expected: "",
1025+
},
1026+
{
1027+
desc: "empty key",
1028+
str: "prefix-" + pvNameMetadata,
1029+
m: map[string]string{"": "pv"},
1030+
expected: "prefix-" + pvNameMetadata,
1031+
},
1032+
{
1033+
desc: "empty value",
1034+
str: "prefix-" + pvNameMetadata,
1035+
m: map[string]string{pvNameMetadata: ""},
1036+
expected: "prefix-",
1037+
},
1038+
{
1039+
desc: "one replacement",
1040+
str: "prefix-" + pvNameMetadata,
1041+
m: map[string]string{pvNameMetadata: "pv"},
1042+
expected: "prefix-pv",
1043+
},
1044+
{
1045+
desc: "multiple replacements",
1046+
str: pvcNamespaceMetadata + pvcNameMetadata,
1047+
m: map[string]string{pvcNamespaceMetadata: "namespace", pvcNameMetadata: "pvcname"},
1048+
expected: "namespacepvcname",
1049+
},
1050+
}
1051+
1052+
for _, test := range tests {
1053+
result := replaceWithMap(test.str, test.m)
1054+
if result != test.expected {
1055+
t.Errorf("test[%s]: unexpected output: %v, expected result: %v", test.desc, result, test.expected)
1056+
}
1057+
}
1058+
}

pkg/blob/controllerserver.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
7474
// set allowBlobPublicAccess as false by default
7575
allowBlobPublicAccess := to.BoolPtr(false)
7676

77+
containerNameReplaceMap := map[string]string{}
78+
7779
// store account key to k8s secret by default
7880
storeAccountKey := true
7981

@@ -121,10 +123,11 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
121123
}
122124
case pvcNamespaceKey:
123125
pvcNamespace = v
126+
containerNameReplaceMap[pvcNamespaceMetadata] = v
124127
case pvcNameKey:
125-
// no op
128+
containerNameReplaceMap[pvcNameMetadata] = v
126129
case pvNameKey:
127-
// no op
130+
containerNameReplaceMap[pvNameMetadata] = v
128131
case serverNameField:
129132
// no op, only used in NodeStageVolume
130133
case storageEndpointSuffixField:
@@ -289,6 +292,8 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
289292
secrets = createStorageAccountSecret(accountName, accountKey)
290293
}
291294

295+
// replace pv/pvc name namespace metadata in subDir
296+
containerName = replaceWithMap(containerName, containerNameReplaceMap)
292297
validContainerName := containerName
293298
if validContainerName == "" {
294299
validContainerName = volName

pkg/blob/nodeserver.go

+12
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
219219

220220
var serverAddress, storageEndpointSuffix, protocol, ephemeralVolMountOptions string
221221
var ephemeralVol, isHnsEnabled bool
222+
223+
containerNameReplaceMap := map[string]string{}
224+
222225
mountPermissions := d.mountPermissions
223226
performChmodOp := (mountPermissions > 0)
224227
for k, v := range attrib {
@@ -235,6 +238,12 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
235238
ephemeralVolMountOptions = v
236239
case isHnsEnabledField:
237240
isHnsEnabled = strings.EqualFold(v, trueValue)
241+
case pvcNamespaceKey:
242+
containerNameReplaceMap[pvcNamespaceMetadata] = v
243+
case pvcNameKey:
244+
containerNameReplaceMap[pvcNameMetadata] = v
245+
case pvNameKey:
246+
containerNameReplaceMap[pvNameMetadata] = v
238247
case mountPermissionsField:
239248
if v != "" {
240249
var err error
@@ -265,6 +274,9 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
265274
return nil, err
266275
}
267276

277+
// replace pv/pvc name namespace metadata in subDir
278+
containerName = replaceWithMap(containerName, containerNameReplaceMap)
279+
268280
if strings.TrimSpace(storageEndpointSuffix) == "" {
269281
if d.cloud.Environment.StorageEndpointSuffix != "" {
270282
storageEndpointSuffix = d.cloud.Environment.StorageEndpointSuffix

test/e2e/dynamic_provisioning_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ var _ = ginkgo.Describe("[blob-csi-e2e] Dynamic Provisioning", func() {
187187
"isHnsEnabled": "true",
188188
"allowBlobPublicAccess": "false",
189189
"useDataPlaneAPI": "true",
190+
"containerName": "container-${pvc.metadata.name}",
190191
},
191192
}
192193
test.Run(cs, ns)
@@ -257,6 +258,7 @@ var _ = ginkgo.Describe("[blob-csi-e2e] Dynamic Provisioning", func() {
257258
StorageClassParameters: map[string]string{
258259
"skuName": "Standard_RAGRS",
259260
"allowBlobPublicAccess": "false",
261+
"containerName": "container-${pvc.metadata.namespace}",
260262
},
261263
}
262264
if isAzureStackCloud {

0 commit comments

Comments
 (0)