Skip to content

Commit fd49af1

Browse files
committed
Add labels validation to match gce requirements
1 parent f00974a commit fd49af1

File tree

5 files changed

+177
-84
lines changed

5 files changed

+177
-84
lines changed

examples/kubernetes/demo-regional-restricted-sc.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ provisioner: pd.csi.storage.gke.io
66
parameters:
77
type: pd-standard
88
replication-type: regional-pd
9-
labels: key1=value1,key2=value2
109
volumeBindingMode: WaitForFirstConsumer
1110
allowedTopologies:
1211
- matchLabelExpressions:

examples/kubernetes/demo-regional-sc.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ provisioner: pd.csi.storage.gke.io
66
parameters:
77
type: pd-standard
88
replication-type: regional-pd
9-
labels: key1=value1,key2=value2
109
volumeBindingMode: WaitForFirstConsumer

pkg/common/utils.go

+35-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package common
1818

1919
import (
2020
"fmt"
21+
"regexp"
2122
"strings"
2223

2324
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
@@ -155,29 +156,55 @@ func ConvertLabelsStringToMap(labels string) (map[string]string, error) {
155156
const labelsDelimiter = ","
156157
const labelsKeyValueDelimiter = "="
157158

158-
m := make(map[string]string)
159+
labelsMap := make(map[string]string)
159160
if labels == "" {
160-
return m, nil
161+
return labelsMap, nil
161162
}
162163

163-
errIncorrectFormat := fmt.Errorf("labels %q are invalid, correct format: 'key1=value1,key2=value2'", labels)
164+
regexKey, _ := regexp.Compile("^[a-z][-_a-z0-9]{0,62}$")
165+
checkLabelKeyFn := func(key string) error {
166+
if !regexKey.MatchString(key) {
167+
return fmt.Errorf("label key %q is invalid (lowercase, digit, _ and - chars are allowed / 1-63 characters", key)
168+
}
169+
return nil
170+
}
171+
172+
regexValue, _ := regexp.Compile("^[-_a-z0-9]{0,63}$")
173+
checkLabelValueFn := func(value string) error {
174+
if !regexValue.MatchString(value) {
175+
return fmt.Errorf("label value %q is invalid (should start with lowercase char / lowercase, digit, _ and - chars are allowed / 0-63 characters", value)
176+
}
177+
178+
return nil
179+
}
164180

165181
keyValueStrings := strings.Split(labels, labelsDelimiter)
166182
for _, keyValue := range keyValueStrings {
167183
keyValue := strings.Split(keyValue, labelsKeyValueDelimiter)
168184

169185
if len(keyValue) != 2 {
170-
return nil, errIncorrectFormat
186+
return nil, fmt.Errorf("labels %q are invalid, correct format: 'key1=value1,key2=value2'", labels)
171187
}
172188

173189
key := strings.TrimSpace(keyValue[0])
174-
if key == "" {
175-
return nil, errIncorrectFormat
190+
if err := checkLabelKeyFn(key); err != nil {
191+
return nil, err
176192
}
177193

178194
value := strings.TrimSpace(keyValue[1])
179-
m[key] = value
195+
if err := checkLabelValueFn(value); err != nil {
196+
return nil, err
197+
}
198+
199+
labelsMap[key] = value
200+
}
201+
202+
const maxNumberOfLabels = 64
203+
if len(labelsMap) > maxNumberOfLabels {
204+
return nil, fmt.Errorf("more than %d labels is not allowed, given: %d", maxNumberOfLabels, len(labelsMap))
180205
}
181206

182-
return m, nil
207+
208+
209+
return labelsMap, nil
183210
}

pkg/common/utils_test.go

+140-72
Original file line numberDiff line numberDiff line change
@@ -297,85 +297,153 @@ func TestKeyToVolumeID(t *testing.T) {
297297
}
298298

299299
func TestConvertLabelsStringToMap(t *testing.T) {
300-
testCases := []struct {
301-
name string
302-
labels string
303-
expectedOutput map[string]string
304-
expectedError bool
305-
}{
306-
{
307-
name: "should return empty map when labels string is empty",
308-
labels: "",
309-
expectedOutput: map[string]string{},
310-
expectedError: false,
311-
},
312-
{
313-
name: "single label string",
314-
labels: "key=value",
315-
expectedOutput: map[string]string{
316-
"key": "value",
300+
t.Run("parsing labels string into map", func(t *testing.T) {
301+
testCases := []struct {
302+
name string
303+
labels string
304+
expectedOutput map[string]string
305+
expectedError bool
306+
}{
307+
{
308+
name: "should return empty map when labels string is empty",
309+
labels: "",
310+
expectedOutput: map[string]string{},
311+
expectedError: false,
317312
},
318-
expectedError: false,
319-
},
320-
{
321-
name: "multiple label string",
322-
labels: "key1=value1,key2=value2",
323-
expectedOutput: map[string]string{
324-
"key1": "value1",
325-
"key2": "value2",
313+
{
314+
name: "single label string",
315+
labels: "key=value",
316+
expectedOutput: map[string]string{
317+
"key": "value",
318+
},
319+
expectedError: false,
326320
},
327-
expectedError: false,
328-
},
329-
{
330-
name: "multiple labels string with whitespaces gets trimmed",
331-
labels: "key1=value1, key2=value2",
332-
expectedOutput: map[string]string{
333-
"key1": "value1",
334-
"key2": "value2",
321+
{
322+
name: "multiple label string",
323+
labels: "key1=value1,key2=value2",
324+
expectedOutput: map[string]string{
325+
"key1": "value1",
326+
"key2": "value2",
327+
},
328+
expectedError: false,
335329
},
336-
expectedError: false,
337-
},
338-
{
339-
name: "malformed labels string (no keys and values)",
340-
labels: ",,",
341-
expectedOutput: nil,
342-
expectedError: true,
343-
},
344-
{
345-
name: "malformed labels string (incorrect format)",
346-
labels: "foo,bar",
347-
expectedOutput: nil,
348-
expectedError: true,
349-
},
350-
{
351-
name: "malformed labels string (missing key)",
352-
labels: "key1=value1,=bar",
353-
expectedOutput: nil,
354-
expectedError: true,
355-
},
356-
{
357-
name: "malformed labels string (missing key and value)",
358-
labels: "key1=value1,=bar,=",
359-
expectedOutput: nil,
360-
expectedError: true,
361-
},
362-
}
330+
{
331+
name: "multiple labels string with whitespaces gets trimmed",
332+
labels: "key1=value1, key2=value2",
333+
expectedOutput: map[string]string{
334+
"key1": "value1",
335+
"key2": "value2",
336+
},
337+
expectedError: false,
338+
},
339+
{
340+
name: "malformed labels string (no keys and values)",
341+
labels: ",,",
342+
expectedOutput: nil,
343+
expectedError: true,
344+
},
345+
{
346+
name: "malformed labels string (incorrect format)",
347+
labels: "foo,bar",
348+
expectedOutput: nil,
349+
expectedError: true,
350+
},
351+
{
352+
name: "malformed labels string (missing key)",
353+
labels: "key1=value1,=bar",
354+
expectedOutput: nil,
355+
expectedError: true,
356+
},
357+
{
358+
name: "malformed labels string (missing key and value)",
359+
labels: "key1=value1,=bar,=",
360+
expectedOutput: nil,
361+
expectedError: true,
362+
},
363+
}
363364

364-
for _, tc := range testCases {
365-
t.Logf("test case: %s", tc.name)
366-
output, err := ConvertLabelsStringToMap(tc.labels)
367-
if err == nil && tc.expectedError {
368-
t.Errorf("Expected error but got none")
365+
for _, tc := range testCases {
366+
t.Logf("test case: %s", tc.name)
367+
output, err := ConvertLabelsStringToMap(tc.labels)
368+
if tc.expectedError && err == nil {
369+
t.Errorf("Expected error but got none")
370+
}
371+
if err != nil {
372+
if !tc.expectedError {
373+
t.Errorf("Did not expect error but got: %v", err)
374+
}
375+
continue
376+
}
377+
378+
if !reflect.DeepEqual(output, tc.expectedOutput) {
379+
t.Errorf("Got labels %v, but expected %v", output, tc.expectedOutput)
380+
}
369381
}
370-
if err != nil {
371-
if !tc.expectedError {
382+
})
383+
384+
t.Run("checking google requirements", func(t *testing.T) {
385+
testCases := []struct {
386+
name string
387+
labels string
388+
expectedError bool
389+
}{
390+
{
391+
name: "64 labels at most",
392+
labels: `k1=v,k2=v,k3=v,k4=v,k5=v,k6=v,k7=v,k8=v,k9=v,k10=v,k11=v,k12=v,k13=v,k14=v,k15=v,k16=v,k17=v,k18=v,k19=v,k20=v,
393+
k21=v,k22=v,k23=v,k24=v,k25=v,k26=v,k27=v,k28=v,k29=v,k30=v,k31=v,k32=v,k33=v,k34=v,k35=v,k36=v,k37=v,k38=v,k39=v,k40=v,
394+
k41=v,k42=v,k43=v,k44=v,k45=v,k46=v,k47=v,k48=v,k49=v,k50=v,k51=v,k52=v,k53=v,k54=v,k55=v,k56=v,k57=v,k58=v,k59=v,k60=v,
395+
k61=v,k62=v,k63=v,k64=v,k65=v`,
396+
expectedError: true,
397+
},
398+
{
399+
name: "label key must start with lowercase char",
400+
labels: "#k=v",
401+
expectedError: true,
402+
},
403+
{
404+
name: "label key can only contain lowercase chars, digits, _ and -)",
405+
labels: "k*=v",
406+
expectedError: true,
407+
},
408+
{
409+
name: "label key may not have over 63 characters",
410+
labels: "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij1234=v",
411+
expectedError: true,
412+
},
413+
{
414+
name: "label key can have up to 63 characters",
415+
labels: "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij123=v",
416+
expectedError: false,
417+
},
418+
{
419+
name: "label value can only contain lowercase chars, digits, _ and -)",
420+
labels: "k1=###",
421+
expectedError: true,
422+
},
423+
{
424+
name: "label value may not have over 63 characters",
425+
labels: "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij1234=v",
426+
expectedError: true,
427+
},
428+
{
429+
name: "label value can have up to 63 characters",
430+
labels: "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij123=v",
431+
expectedError: false,
432+
},
433+
}
434+
435+
for _, tc := range testCases {
436+
t.Logf("test case: %s", tc.name)
437+
_, err := ConvertLabelsStringToMap(tc.labels)
438+
439+
if tc.expectedError && err == nil {
440+
t.Errorf("Expected error but got none")
441+
}
442+
443+
if !tc.expectedError && err != nil {
372444
t.Errorf("Did not expect error but got: %v", err)
373445
}
374-
continue
375446
}
447+
})
376448

377-
if !reflect.DeepEqual(output, tc.expectedOutput) {
378-
t.Errorf("Got labels %v, but expected %v", output, tc.expectedOutput)
379-
}
380-
}
381449
}

pkg/gce-pd-csi-driver/controller_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -684,12 +684,12 @@ func TestCreateVolumeArguments(t *testing.T) {
684684
},
685685
},
686686
{
687-
name: "success with malformed labels parameter",
687+
name: "fail with malformed labels parameter",
688688
req: &csi.CreateVolumeRequest{
689689
Name: name,
690690
CapacityRange: stdCapRange,
691691
VolumeCapabilities: stdVolCaps,
692-
Parameters: map[string]string{"labels": "key1=value1,;;"},
692+
Parameters: map[string]string{"labels": "key1=value1,#=$;;"},
693693
},
694694
expErrCode: codes.InvalidArgument,
695695
},

0 commit comments

Comments
 (0)