diff --git a/SPEC.md b/SPEC.md index 06d374c..1914aaf 100644 --- a/SPEC.md +++ b/SPEC.md @@ -27,6 +27,7 @@ Released versions of the spec are available as Git tags. | v0.4.0 | | Added `type` field to Mount specification | | v0.5.0 | | Add `HostPath` to `DeviceNodes` | | v0.6.0 | | Add `Annotations` field to `Spec` and `Device` specifications | +| | | Allow dots (`.`) in name segment of `Kind` field | *Note*: The initial release of a **spec** with version `v0.x.0` will be tagged as `v0.x.0` with subsequent changes to the API applicable to this version tagged as `v0.x.y`. diff --git a/pkg/cdi/qualified-device.go b/pkg/cdi/qualified-device.go index dee2717..1a30e10 100644 --- a/pkg/cdi/qualified-device.go +++ b/pkg/cdi/qualified-device.go @@ -25,14 +25,10 @@ import ( // // "/=". // -// A valid vendor name may contain the following runes: +// A valid vendor and class name may contain the following runes: // // 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'. // -// A valid class name may contain the following runes: -// -// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_'. -// // A valid device name may containe the following runes: // // 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':' @@ -98,7 +94,7 @@ func ValidateVendorName(vendor string) error { // A class name may contain the following ASCII characters: // - upper- and lowercase letters ('A'-'Z', 'a'-'z') // - digits ('0'-'9') -// - underscore and dash ('_', '-') +// - underscore, dash, and dot ('_', '-', and '.') // // Deprecated: use parser.ValidateClassName instead func ValidateClassName(class string) error { diff --git a/pkg/cdi/qualified-device_test.go b/pkg/cdi/qualified-device_test.go index 9a63e5f..c4f3f2f 100644 --- a/pkg/cdi/qualified-device_test.go +++ b/pkg/cdi/qualified-device_test.go @@ -54,6 +54,13 @@ func TestQualifiedName(t *testing.T) { name: "dev1", isQualified: true, }, + { + device: "vendor1.com/class.subclass=dev1", + vendor: "vendor1.com", + class: "class.subclass", + name: "dev1", + isQualified: true, + }, { device: "other-vendor1.com/class_1=dev_1", vendor: "other-vendor1.com", diff --git a/pkg/cdi/spec_test.go b/pkg/cdi/spec_test.go index 3422d46..24c27f8 100644 --- a/pkg/cdi/spec_test.go +++ b/pkg/cdi/spec_test.go @@ -662,6 +662,13 @@ func TestRequiredVersion(t *testing.T) { }, expectedVersion: "0.6.0", }, + { + description: "dotted name (class) label require v0.6.0", + spec: &cdi.Spec{ + Kind: "vendor.com/class.sub", + }, + expectedVersion: "0.6.0", + }, } for _, tc := range testCases { diff --git a/pkg/cdi/version.go b/pkg/cdi/version.go index 955147c..1234258 100644 --- a/pkg/cdi/version.go +++ b/pkg/cdi/version.go @@ -132,6 +132,14 @@ func requiresV060(spec *cdi.Spec) bool { } } + // The v0.6.0 spec allows dots "." in Kind name label (class) + vendor, class := parser.ParseQualifier(spec.Kind) + if vendor != "" { + if strings.ContainsRune(class, '.') { + return true + } + } + return false } diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 73fe98f..280162f 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -26,14 +26,10 @@ import ( // // "/=". // -// A valid vendor name may contain the following runes: +// A valid vendor and class name may contain the following runes: // // 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'. // -// A valid class name may contain the following runes: -// -// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_'. -// // A valid device name may containe the following runes: // // 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':' @@ -122,52 +118,51 @@ func ParseQualifier(kind string) (string, string) { // - digits ('0'-'9') // - underscore, dash, and dot ('_', '-', and '.') func ValidateVendorName(vendor string) error { - if vendor == "" { - return fmt.Errorf("invalid (empty) vendor name") - } - if !IsLetter(rune(vendor[0])) { - return fmt.Errorf("invalid vendor %q, should start with letter", vendor) - } - for _, c := range string(vendor[1 : len(vendor)-1]) { - switch { - case IsAlphaNumeric(c): - case c == '_' || c == '-' || c == '.': - default: - return fmt.Errorf("invalid character '%c' in vendor name %q", - c, vendor) - } - } - if !IsAlphaNumeric(rune(vendor[len(vendor)-1])) { - return fmt.Errorf("invalid vendor %q, should end with a letter or digit", vendor) + err := validateVendorOrClassName(vendor) + if err != nil { + err = fmt.Errorf("invalid vendor. %w", err) } - - return nil + return err } // ValidateClassName checks the validity of class name. // A class name may contain the following ASCII characters: // - upper- and lowercase letters ('A'-'Z', 'a'-'z') // - digits ('0'-'9') -// - underscore and dash ('_', '-') +// - underscore, dash, and dot ('_', '-', and '.') func ValidateClassName(class string) error { - if class == "" { - return fmt.Errorf("invalid (empty) device class") + err := validateVendorOrClassName(class) + if err != nil { + err = fmt.Errorf("invalid class. %w", err) } - if !IsLetter(rune(class[0])) { - return fmt.Errorf("invalid class %q, should start with letter", class) + return err +} + +// validateVendorOrClassName checks the validity of vendor or class name. +// A name may contain the following ASCII characters: +// - upper- and lowercase letters ('A'-'Z', 'a'-'z') +// - digits ('0'-'9') +// - underscore, dash, and dot ('_', '-', and '.') +func validateVendorOrClassName(name string) error { + if name == "" { + return fmt.Errorf("empty name") } - for _, c := range string(class[1 : len(class)-1]) { + if !IsLetter(rune(name[0])) { + return fmt.Errorf("%q, should start with letter", name) + } + for _, c := range string(name[1 : len(name)-1]) { switch { case IsAlphaNumeric(c): - case c == '_' || c == '-': + case c == '_' || c == '-' || c == '.': default: - return fmt.Errorf("invalid character '%c' in device class %q", - c, class) + return fmt.Errorf("invalid character '%c' in name %q", + c, name) } } - if !IsAlphaNumeric(rune(class[len(class)-1])) { - return fmt.Errorf("invalid class %q, should end with a letter or digit", class) + if !IsAlphaNumeric(rune(name[len(name)-1])) { + return fmt.Errorf("%q, should end with a letter or digit", name) } + return nil } diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go index b13b5fa..ecd9138 100644 --- a/pkg/parser/parser_test.go +++ b/pkg/parser/parser_test.go @@ -54,6 +54,13 @@ func TestQualifiedName(t *testing.T) { name: "dev1", isQualified: true, }, + { + device: "vendor1.com/class.subclass=dev1", + vendor: "vendor1.com", + class: "class.subclass", + name: "dev1", + isQualified: true, + }, { device: "other-vendor1.com/class_1=dev_1", vendor: "other-vendor1.com",