Skip to content

Commit 3040569

Browse files
Merge pull request #559 from chrisThePattyEater/main
Allow multiple volume handles with different mount options
2 parents bd50eca + 2ed24c7 commit 3040569

3 files changed

Lines changed: 63 additions & 2 deletions

File tree

pkg/csi_driver/controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ func (s *controllerServer) ControllerGetCapabilities(_ context.Context, _ *csi.C
7676
func (s *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
7777
// Validate arguments
7878
volumeID := req.GetVolumeId()
79+
if req.GetVolumeContext()[VolumeContextKeyEphemeral] != util.TrueStr {
80+
volumeID = parseVolumeID(volumeID)
81+
}
7982
if len(volumeID) == 0 {
8083
return nil, status.Error(codes.InvalidArgument, "ValidateVolumeCapabilities volumeID must be provided")
8184
}

pkg/csi_driver/utils.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ const (
6868
tokenServerSidecarMinVersion = "v1.12.2-gke.0" // #nosec G101
6969
)
7070

71+
var volumeIDRegEx = regexp.MustCompile(`:.*$`)
72+
7173
func NewVolumeCapabilityAccessMode(mode csi.VolumeCapability_AccessMode_Mode) *csi.VolumeCapability_AccessMode {
7274
return &csi.VolumeCapability_AccessMode{Mode: mode}
7375
}
@@ -271,14 +273,13 @@ func parseRequestArguments(req *csi.NodePublishVolumeRequest) (string, string, [
271273
}
272274

273275
vc := req.GetVolumeContext()
274-
bucketName := req.GetVolumeId()
276+
bucketName := parseVolumeID(req.GetVolumeId())
275277
if vc[VolumeContextKeyEphemeral] == util.TrueStr {
276278
bucketName = vc[VolumeContextKeyBucketName]
277279
if len(bucketName) == 0 {
278280
return "", "", nil, false, false, fmt.Errorf("NodePublishVolume VolumeContext %q must be provided for ephemeral storage", VolumeContextKeyBucketName)
279281
}
280282
}
281-
282283
fuseMountOptions := []string{}
283284
if req.GetReadonly() {
284285
fuseMountOptions = joinMountOptions(fuseMountOptions, []string{"ro"})
@@ -302,6 +303,13 @@ func parseRequestArguments(req *csi.NodePublishVolumeRequest) (string, string, [
302303
return targetPath, bucketName, fuseMountOptions, skipCSIBucketAccessCheck, enableMetricsCollection, nil
303304
}
304305

306+
// The format allows customers to specify a fake volume handle for static provisioning,
307+
// enabling multiple PVs in the same pod to mount the same bucket. This prevents Kubelet from
308+
// skipping mounts of volumes with the same volume handle, which can cause the pod to be stuck in container creation.
309+
func parseVolumeID(bucketHandle string) string {
310+
return volumeIDRegEx.ReplaceAllString(bucketHandle, "")
311+
}
312+
305313
func putExitFile(pod *corev1.Pod, targetPath string) error {
306314
podIsTerminating := pod.DeletionTimestamp != nil
307315
podRestartPolicyIsNever := pod.Spec.RestartPolicy == corev1.RestartPolicyNever

pkg/csi_driver/utils_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,56 @@ const (
2929
TraceStr = "trace"
3030
)
3131

32+
func TestRemoveBucketSuffixIfPresentAndReturnVolumeId(t *testing.T) {
33+
t.Parallel()
34+
t.Run("removing bucket suffix if present and returning volume id", func(t *testing.T) {
35+
t.Parallel()
36+
testCases := []struct {
37+
name string
38+
bucketName string
39+
expectedValue string
40+
}{
41+
{
42+
name: "should return bucket name without suffix, no special character",
43+
bucketName: "bucket-name:1234567890123456789",
44+
expectedValue: "bucket-name",
45+
},
46+
{
47+
name: "should return bucket name without suffix, special character",
48+
bucketName: "bucket-name:1234567890123456789@us-central1",
49+
expectedValue: "bucket-name",
50+
},
51+
{
52+
name: "should return bucket name without suffix, no colon with special character",
53+
bucketName: "bucket-name@us-central1",
54+
expectedValue: "bucket-name@us-central1",
55+
},
56+
{
57+
name: "should return bucket name without suffix, two colons",
58+
bucketName: "bucket-name:1234567890123456789:12",
59+
expectedValue: "bucket-name",
60+
},
61+
{
62+
name: "should return bucket name without suffix, no special character and no colon",
63+
bucketName: "bucket-name",
64+
expectedValue: "bucket-name",
65+
},
66+
{
67+
name: "should return bucket name without suffix, no bucket name",
68+
bucketName: ":xyz",
69+
expectedValue: "",
70+
},
71+
}
72+
73+
for _, tc := range testCases {
74+
t.Logf("test case: %s", tc.name)
75+
actual := parseVolumeID(tc.bucketName)
76+
if actual != tc.expectedValue {
77+
t.Errorf("Got value %v, but expected %v", actual, tc.expectedValue)
78+
}
79+
}
80+
})
81+
}
3282
func TestJoinMountOptions(t *testing.T) {
3383
t.Parallel()
3484
t.Run("joining mount options into one", func(t *testing.T) {

0 commit comments

Comments
 (0)