Skip to content

Commit fb588c5

Browse files
committed
Add api/validator submodule
Signed-off-by: Evan Lezar <[email protected]>
1 parent 4149a8a commit fb588c5

File tree

22 files changed

+889
-250
lines changed

22 files changed

+889
-250
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
limitations under the License.
1515
*/
1616

17-
package validation
17+
package validator
1818

1919
import (
2020
"fmt"
2121
"strings"
2222

23-
"tags.cncf.io/container-device-interface/internal/validation/k8s"
23+
"tags.cncf.io/container-device-interface/api/validator/k8s"
2424
)
2525

2626
// ValidateSpecAnnotations checks whether spec annotations are valid.

api/validator/api.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
Copyright © 2024 The CDI 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 validator
18+
19+
import "errors"
20+
21+
// Validators as constants.
22+
const (
23+
Default = defaultValidator("default")
24+
Disabled = disabledValidator("disabled")
25+
)
26+
27+
// ErrInvalid can be returned if CDI spec validation fails.
28+
var ErrInvalid = errors.New("invalid CDI specification")

api/validator/go.mod

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module tags.cncf.io/container-device-interface/api/validator
2+
3+
go 1.20
4+
5+
require (
6+
github.com/stretchr/testify v1.7.0
7+
tags.cncf.io/container-device-interface/specs-go v0.8.0
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
golang.org/x/mod v0.19.0 // indirect
14+
gopkg.in/yaml.v3 v3.0.1 // indirect
15+
)
16+
17+
replace tags.cncf.io/container-device-interface/specs-go => ../../specs-go

api/validator/go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
8+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
9+
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
10+
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
11+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
12+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
13+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
14+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
15+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

api/validator/identifiers.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
Copyright © 2024 The CDI 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 validator
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
)
23+
24+
// ValidateKind checks the validity of a CDI kind.
25+
// The syntax for a device kind“ is
26+
//
27+
// "<vendor>/<class>"
28+
func ValidateKind(kind string) error {
29+
parts := strings.SplitN(kind, "/", 2)
30+
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
31+
return fmt.Errorf("kind %s does not contain a / %w", kind, ErrInvalid)
32+
}
33+
if err := ValidateVendorName(parts[0]); err != nil {
34+
return err
35+
}
36+
if err := ValidateClassName(parts[1]); err != nil {
37+
return err
38+
}
39+
return nil
40+
}
41+
42+
// ValidateVendorName checks the validity of a vendor name.
43+
// A vendor name may contain the following ASCII characters:
44+
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
45+
// - digits ('0'-'9')
46+
// - underscore, dash, and dot ('_', '-', and '.')
47+
func ValidateVendorName(vendor string) error {
48+
err := validateVendorOrClassName(vendor)
49+
if err != nil {
50+
err = fmt.Errorf("invalid vendor. %w", err)
51+
}
52+
return err
53+
}
54+
55+
// ValidateClassName checks the validity of class name.
56+
// A class name may contain the following ASCII characters:
57+
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
58+
// - digits ('0'-'9')
59+
// - underscore, dash, and dot ('_', '-', and '.')
60+
func ValidateClassName(class string) error {
61+
err := validateVendorOrClassName(class)
62+
if err != nil {
63+
err = fmt.Errorf("invalid class. %w", err)
64+
}
65+
return err
66+
}
67+
68+
// validateVendorOrClassName checks the validity of vendor or class name.
69+
// A name may contain the following ASCII characters:
70+
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
71+
// - digits ('0'-'9')
72+
// - underscore, dash, and dot ('_', '-', and '.')
73+
func validateVendorOrClassName(name string) error {
74+
if name == "" {
75+
return fmt.Errorf("empty name")
76+
}
77+
if !IsLetter(rune(name[0])) {
78+
return fmt.Errorf("%q, should start with letter", name)
79+
}
80+
for _, c := range string(name[1 : len(name)-1]) {
81+
switch {
82+
case IsAlphaNumeric(c):
83+
case c == '_' || c == '-' || c == '.':
84+
default:
85+
return fmt.Errorf("invalid character '%c' in name %q",
86+
c, name)
87+
}
88+
}
89+
if !IsAlphaNumeric(rune(name[len(name)-1])) {
90+
return fmt.Errorf("%q, should end with a letter or digit", name)
91+
}
92+
93+
return nil
94+
}
95+
96+
// ValidateDeviceName checks the validity of a device name.
97+
// A device name may contain the following ASCII characters:
98+
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
99+
// - digits ('0'-'9')
100+
// - underscore, dash, dot, colon ('_', '-', '.', ':')
101+
func ValidateDeviceName(name string) error {
102+
if name == "" {
103+
return fmt.Errorf("invalid (empty) device name")
104+
}
105+
if !IsAlphaNumeric(rune(name[0])) {
106+
return fmt.Errorf("invalid class %q, should start with a letter or digit", name)
107+
}
108+
if len(name) == 1 {
109+
return nil
110+
}
111+
for _, c := range string(name[1 : len(name)-1]) {
112+
switch {
113+
case IsAlphaNumeric(c):
114+
case c == '_' || c == '-' || c == '.' || c == ':':
115+
default:
116+
return fmt.Errorf("invalid character '%c' in device name %q",
117+
c, name)
118+
}
119+
}
120+
if !IsAlphaNumeric(rune(name[len(name)-1])) {
121+
return fmt.Errorf("invalid name %q, should end with a letter or digit", name)
122+
}
123+
return nil
124+
}
125+
126+
// IsLetter reports whether the rune is a letter.
127+
func IsLetter(c rune) bool {
128+
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
129+
}
130+
131+
// IsDigit reports whether the rune is a digit.
132+
func IsDigit(c rune) bool {
133+
return '0' <= c && c <= '9'
134+
}
135+
136+
// IsAlphaNumeric reports whether the rune is a letter or digit.
137+
func IsAlphaNumeric(c rune) bool {
138+
return IsLetter(c) || IsDigit(c)
139+
}

0 commit comments

Comments
 (0)