From 58bb34aec4fbae6f1f8d592c724c92a9289a5656 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Mon, 29 Jul 2024 17:22:41 +0300 Subject: [PATCH 1/7] Update golang.org/x/mod dependency Signed-off-by: Ed Bartosh --- cmd/cdi/go.mod | 2 +- cmd/cdi/go.sum | 15 ++------------- cmd/validate/go.sum | 2 +- go.mod | 2 +- go.sum | 15 ++------------- pkg/cdi/qualified-device.go | 13 ------------- pkg/cdi/spec.go | 7 ++++--- pkg/parser/parser.go | 10 ++++------ 8 files changed, 15 insertions(+), 51 deletions(-) diff --git a/cmd/cdi/go.mod b/cmd/cdi/go.mod index efac1501..5635434d 100644 --- a/cmd/cdi/go.mod +++ b/cmd/cdi/go.mod @@ -19,7 +19,7 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - golang.org/x/mod v0.4.2 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/sys v0.1.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect diff --git a/cmd/cdi/go.sum b/cmd/cdi/go.sum index b4892fc3..58bedc5e 100644 --- a/cmd/cdi/go.sum +++ b/cmd/cdi/go.sum @@ -45,24 +45,13 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/cmd/validate/go.sum b/cmd/validate/go.sum index 8ba012f2..578a08c5 100644 --- a/cmd/validate/go.sum +++ b/cmd/validate/go.sum @@ -16,7 +16,7 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go.mod b/go.mod index d0e554fb..8cda9399 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 github.com/stretchr/testify v1.7.0 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/mod v0.4.2 golang.org/x/sys v0.1.0 sigs.k8s.io/yaml v1.3.0 tags.cncf.io/container-device-interface/specs-go v0.8.0 @@ -20,6 +19,7 @@ require ( github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + golang.org/x/mod v0.19.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d7ac8665..beba3859 100644 --- a/go.sum +++ b/go.sum @@ -37,24 +37,13 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/pkg/cdi/qualified-device.go b/pkg/cdi/qualified-device.go index 0bdfdc16..49099c8f 100644 --- a/pkg/cdi/qualified-device.go +++ b/pkg/cdi/qualified-device.go @@ -66,19 +66,6 @@ func ParseDevice(device string) (string, string, string) { return parser.ParseDevice(device) } -// ParseQualifier splits a device qualifier into vendor and class. -// The syntax for a device qualifier is -// -// "/" -// -// If parsing fails, an empty vendor and the class set to the -// verbatim input is returned. -// -// Deprecated: use parser.ParseQualifier instead -func ParseQualifier(kind string) (string, string) { - return parser.ParseQualifier(kind) -} - // ValidateVendorName checks the validity of a vendor name. // A vendor name may contain the following ASCII characters: // - upper- and lowercase letters ('A'-'Z', 'a'-'z') diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index 1a0a662b..47094964 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -28,6 +28,7 @@ import ( "sigs.k8s.io/yaml" "tags.cncf.io/container-device-interface/internal/validation" + "tags.cncf.io/container-device-interface/pkg/parser" cdi "tags.cncf.io/container-device-interface/specs-go" ) @@ -105,7 +106,7 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { spec.path += defaultSpecExt } - spec.vendor, spec.class = ParseQualifier(spec.Kind) + spec.vendor, spec.class = parser.ParseQualifier(spec.Kind) if spec.devices, err = spec.validate(); err != nil { return nil, fmt.Errorf("invalid CDI Spec: %w", err) @@ -328,7 +329,7 @@ func GenerateTransientSpecName(vendor, class, transientID string) string { // the Spec does not contain a valid vendor or class, it returns // an empty name and a non-nil error. func GenerateNameForSpec(raw *cdi.Spec) (string, error) { - vendor, class := ParseQualifier(raw.Kind) + vendor, class := parser.ParseQualifier(raw.Kind) if vendor == "" { return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind) } @@ -342,7 +343,7 @@ func GenerateNameForSpec(raw *cdi.Spec) (string, error) { // If the Spec does not contain a valid vendor or class, it returns an // an empty name and a non-nil error. func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) { - vendor, class := ParseQualifier(raw.Kind) + vendor, class := parser.ParseQualifier(raw.Kind) if vendor == "" { return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind) } diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 53259895..d307ff55 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -19,6 +19,8 @@ package parser import ( "fmt" "strings" + + specs "tags.cncf.io/container-device-interface/specs-go" ) // QualifiedName returns the qualified name for a device. @@ -105,11 +107,7 @@ func ParseDevice(device string) (string, string, string) { // If parsing fails, an empty vendor and the class set to the // verbatim input is returned. func ParseQualifier(kind string) (string, string) { - parts := strings.SplitN(kind, "/", 2) - if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", kind - } - return parts[0], parts[1] + return specs.ParseQualifier(kind) } // ValidateVendorName checks the validity of a vendor name. @@ -198,7 +196,7 @@ func ValidateDeviceName(name string) error { // IsLetter reports whether the rune is a letter. func IsLetter(c rune) bool { - return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') + return specs.IsLetter(c) } // IsDigit reports whether the rune is a digit. From 1e39659e8626d0d19c49bbc07d40524eb95ee5e0 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Mon, 29 Jul 2024 17:17:41 +0300 Subject: [PATCH 2/7] move version APIs to the specs-go Signed-off-by: Ed Bartosh --- pkg/cdi/spec.go | 15 ++---- pkg/cdi/spec_test.go | 4 +- specs-go/config.go | 3 -- specs-go/go.mod | 2 + specs-go/go.sum | 2 + specs-go/parser.go | 39 +++++++++++++++ {pkg/cdi => specs-go}/version.go | 82 +++++++++++++++++--------------- 7 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 specs-go/parser.go rename {pkg/cdi => specs-go}/version.go (74%) diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index 47094964..c0e04321 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -203,15 +203,15 @@ func (s *Spec) edits() *ContainerEdits { // Validate the Spec. func (s *Spec) validate() (map[string]*Device, error) { - if err := validateVersion(s.Version); err != nil { + if err := cdi.ValidateVersion(s.Version); err != nil { return nil, err } - minVersion, err := MinimumRequiredVersion(s.Spec) + minVersion, err := cdi.MinimumRequiredVersion(s.Spec) if err != nil { return nil, fmt.Errorf("could not determine minimum required version: %v", err) } - if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) { + if cdi.NewVersion(minVersion).IsGreaterThan(cdi.NewVersion(s.Version)) { return nil, fmt.Errorf("the spec version must be at least v%v", minVersion) } @@ -243,15 +243,6 @@ func (s *Spec) validate() (map[string]*Device, error) { return devices, nil } -// validateVersion checks whether the specified spec version is supported. -func validateVersion(version string) error { - if !validSpecVersions.isValidVersion(version) { - return fmt.Errorf("invalid version %q", version) - } - - return nil -} - // ParseSpec parses CDI Spec data into a raw CDI Spec. func ParseSpec(data []byte) (*cdi.Spec, error) { var raw *cdi.Spec diff --git a/pkg/cdi/spec_test.go b/pkg/cdi/spec_test.go index 0f5026a9..df7ff683 100644 --- a/pkg/cdi/spec_test.go +++ b/pkg/cdi/spec_test.go @@ -525,7 +525,7 @@ func specType(content []byte) string { } func TestCurrentVersionIsValid(t *testing.T) { - require.NoError(t, validateVersion(cdi.CurrentVersion)) + require.NoError(t, cdi.ValidateVersion(cdi.CurrentVersion)) } func TestRequiredVersion(t *testing.T) { @@ -723,7 +723,7 @@ func TestRequiredVersion(t *testing.T) { for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { - v, err := MinimumRequiredVersion(tc.spec) + v, err := cdi.MinimumRequiredVersion(tc.spec) require.NoError(t, err) require.Equal(t, tc.expectedVersion, v) diff --git a/specs-go/config.go b/specs-go/config.go index d6d6302f..aae112ab 100644 --- a/specs-go/config.go +++ b/specs-go/config.go @@ -2,9 +2,6 @@ package specs import "os" -// CurrentVersion is the current version of the Spec. -const CurrentVersion = "0.8.0" - // Spec is the base configuration for CDI type Spec struct { Version string `json:"cdiVersion"` diff --git a/specs-go/go.mod b/specs-go/go.mod index 9b19b9ef..0b0916df 100644 --- a/specs-go/go.mod +++ b/specs-go/go.mod @@ -1,3 +1,5 @@ module tags.cncf.io/container-device-interface/specs-go go 1.19 + +require golang.org/x/mod v0.19.0 diff --git a/specs-go/go.sum b/specs-go/go.sum index e69de29b..f83cba5a 100644 --- a/specs-go/go.sum +++ b/specs-go/go.sum @@ -0,0 +1,2 @@ +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= diff --git a/specs-go/parser.go b/specs-go/parser.go new file mode 100644 index 00000000..7bdf0ea4 --- /dev/null +++ b/specs-go/parser.go @@ -0,0 +1,39 @@ +/* + Copyright © The CDI Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package specs + +import "strings" + +// ParseQualifier splits a device qualifier into vendor and class. +// The syntax for a device qualifier is +// +// "/" +// +// If parsing fails, an empty vendor and the class set to the +// verbatim input is returned. +func ParseQualifier(kind string) (string, string) { + parts := strings.SplitN(kind, "/", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", kind + } + return parts[0], parts[1] +} + +// IsLetter reports whether the rune is a letter. +func IsLetter(c rune) bool { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') +} diff --git a/pkg/cdi/version.go b/specs-go/version.go similarity index 74% rename from pkg/cdi/version.go rename to specs-go/version.go index 9ca91267..bb7eaa68 100644 --- a/pkg/cdi/version.go +++ b/specs-go/version.go @@ -14,36 +14,34 @@ limitations under the License. */ -package cdi +package specs import ( + "fmt" "strings" "golang.org/x/mod/semver" - - "tags.cncf.io/container-device-interface/pkg/parser" - cdi "tags.cncf.io/container-device-interface/specs-go" ) const ( - // CurrentVersion is the current version of the CDI Spec. - CurrentVersion = cdi.CurrentVersion + // CurrentVersion is the current version of the Spec. + CurrentVersion = "0.8.0" // vCurrent is the current version as a semver-comparable type - vCurrent version = "v" + CurrentVersion + vCurrent Version = "v" + CurrentVersion // These represent the released versions of the CDI specification - v010 version = "v0.1.0" - v020 version = "v0.2.0" - v030 version = "v0.3.0" - v040 version = "v0.4.0" - v050 version = "v0.5.0" - v060 version = "v0.6.0" - v070 version = "v0.7.0" - v080 version = "v0.8.0" + v010 Version = "v0.1.0" + v020 Version = "v0.2.0" + v030 Version = "v0.3.0" + v040 Version = "v0.4.0" + v050 Version = "v0.5.0" + v060 Version = "v0.6.0" + v070 Version = "v0.7.0" + v080 Version = "v0.8.0" // vEarliest is the earliest supported version of the CDI specification - vEarliest version = v030 + vEarliest Version = v030 ) // validSpecVersions stores a map of spec versions to functions to check the required versions. @@ -60,50 +58,58 @@ var validSpecVersions = requiredVersionMap{ v080: requiresV080, } +// ValidateVersion checks whether the specified spec version is supported. +func ValidateVersion(version string) error { + if !validSpecVersions.isValidVersion(version) { + return fmt.Errorf("invalid version %q", version) + } + return nil +} + // MinimumRequiredVersion determines the minimum spec version for the input spec. -func MinimumRequiredVersion(spec *cdi.Spec) (string, error) { +func MinimumRequiredVersion(spec *Spec) (string, error) { minVersion := validSpecVersions.requiredVersion(spec) return minVersion.String(), nil } -// version represents a semantic version string -type version string +// Version represents a semantic version string +type Version string -// newVersion creates a version that can be used for semantic version comparisons. -func newVersion(v string) version { - return version("v" + strings.TrimPrefix(v, "v")) +// NewVersion creates a version that can be used for semantic version comparisons. +func NewVersion(v string) Version { + return Version("v" + strings.TrimPrefix(v, "v")) } // String returns the string representation of the version. // This trims a leading v if present. -func (v version) String() string { +func (v Version) String() string { return strings.TrimPrefix(string(v), "v") } // IsGreaterThan checks with a version is greater than the specified version. -func (v version) IsGreaterThan(o version) bool { +func (v Version) IsGreaterThan(o Version) bool { return semver.Compare(string(v), string(o)) > 0 } // IsLatest checks whether the version is the latest supported version -func (v version) IsLatest() bool { +func (v Version) IsLatest() bool { return v == vCurrent } -type requiredFunc func(*cdi.Spec) bool +type requiredFunc func(*Spec) bool -type requiredVersionMap map[version]requiredFunc +type requiredVersionMap map[Version]requiredFunc // isValidVersion checks whether the specified version is valid. // A version is valid if it is contained in the required version map. func (r requiredVersionMap) isValidVersion(specVersion string) bool { - _, ok := validSpecVersions[newVersion(specVersion)] + _, ok := validSpecVersions[NewVersion(specVersion)] return ok } // requiredVersion returns the minimum version required for the given spec -func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version { +func (r requiredVersionMap) requiredVersion(spec *Spec) Version { minVersion := vEarliest for v, isRequired := range validSpecVersions { @@ -125,12 +131,12 @@ func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version { // requiresV080 returns true if the spec uses v0.8.0 features. // Since the v0.8.0 spec bump was due to the removed .ToOCI functions on the // spec types, there are explicit spec changes. -func requiresV080(_ *cdi.Spec) bool { +func requiresV080(_ *Spec) bool { return false } // requiresV070 returns true if the spec uses v0.7.0 features -func requiresV070(spec *cdi.Spec) bool { +func requiresV070(spec *Spec) bool { if spec.ContainerEdits.IntelRdt != nil { return true } @@ -153,7 +159,7 @@ func requiresV070(spec *cdi.Spec) bool { } // requiresV060 returns true if the spec uses v0.6.0 features -func requiresV060(spec *cdi.Spec) bool { +func requiresV060(spec *Spec) bool { // The v0.6.0 spec allows annotations to be specified at a spec level for range spec.Annotations { return true @@ -167,7 +173,7 @@ func requiresV060(spec *cdi.Spec) bool { } // The v0.6.0 spec allows dots "." in Kind name label (class) - vendor, class := parser.ParseQualifier(spec.Kind) + vendor, class := ParseQualifier(spec.Kind) if vendor != "" { if strings.ContainsRune(class, '.') { return true @@ -178,12 +184,12 @@ func requiresV060(spec *cdi.Spec) bool { } // requiresV050 returns true if the spec uses v0.5.0 features -func requiresV050(spec *cdi.Spec) bool { - var edits []*cdi.ContainerEdits +func requiresV050(spec *Spec) bool { + var edits []*ContainerEdits for _, d := range spec.Devices { // The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter - if len(d.Name) > 0 && !parser.IsLetter(rune(d.Name[0])) { + if len(d.Name) > 0 && !IsLetter(rune(d.Name[0])) { return true } edits = append(edits, &d.ContainerEdits) @@ -202,8 +208,8 @@ func requiresV050(spec *cdi.Spec) bool { } // requiresV040 returns true if the spec uses v0.4.0 features -func requiresV040(spec *cdi.Spec) bool { - var edits []*cdi.ContainerEdits +func requiresV040(spec *Spec) bool { + var edits []*ContainerEdits for _, d := range spec.Devices { edits = append(edits, &d.ContainerEdits) From ab28e02e61acb9414be300e3480d9a4c7b6c7cef Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Tue, 6 Aug 2024 17:50:35 +0300 Subject: [PATCH 3/7] make version a private type Signed-off-by: Ed Bartosh Co-authored-by: Evan Lezar --- specs-go/version.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/specs-go/version.go b/specs-go/version.go index bb7eaa68..05c72dde 100644 --- a/specs-go/version.go +++ b/specs-go/version.go @@ -28,20 +28,20 @@ const ( CurrentVersion = "0.8.0" // vCurrent is the current version as a semver-comparable type - vCurrent Version = "v" + CurrentVersion + vCurrent version = "v" + CurrentVersion // These represent the released versions of the CDI specification - v010 Version = "v0.1.0" - v020 Version = "v0.2.0" - v030 Version = "v0.3.0" - v040 Version = "v0.4.0" - v050 Version = "v0.5.0" - v060 Version = "v0.6.0" - v070 Version = "v0.7.0" - v080 Version = "v0.8.0" + v010 version = "v0.1.0" + v020 version = "v0.2.0" + v030 version = "v0.3.0" + v040 version = "v0.4.0" + v050 version = "v0.5.0" + v060 version = "v0.6.0" + v070 version = "v0.7.0" + v080 version = "v0.8.0" // vEarliest is the earliest supported version of the CDI specification - vEarliest Version = v030 + vEarliest version = v030 ) // validSpecVersions stores a map of spec versions to functions to check the required versions. @@ -72,44 +72,44 @@ func MinimumRequiredVersion(spec *Spec) (string, error) { return minVersion.String(), nil } -// Version represents a semantic version string -type Version string +// version represents a semantic version string +type version string -// NewVersion creates a version that can be used for semantic version comparisons. -func NewVersion(v string) Version { - return Version("v" + strings.TrimPrefix(v, "v")) +// newVersion creates a version that can be used for semantic version comparisons. +func newVersion(v string) version { + return version("v" + strings.TrimPrefix(v, "v")) } // String returns the string representation of the version. // This trims a leading v if present. -func (v Version) String() string { +func (v version) String() string { return strings.TrimPrefix(string(v), "v") } // IsGreaterThan checks with a version is greater than the specified version. -func (v Version) IsGreaterThan(o Version) bool { +func (v version) IsGreaterThan(o version) bool { return semver.Compare(string(v), string(o)) > 0 } // IsLatest checks whether the version is the latest supported version -func (v Version) IsLatest() bool { +func (v version) IsLatest() bool { return v == vCurrent } type requiredFunc func(*Spec) bool -type requiredVersionMap map[Version]requiredFunc +type requiredVersionMap map[version]requiredFunc // isValidVersion checks whether the specified version is valid. // A version is valid if it is contained in the required version map. func (r requiredVersionMap) isValidVersion(specVersion string) bool { - _, ok := validSpecVersions[NewVersion(specVersion)] + _, ok := validSpecVersions[newVersion(specVersion)] return ok } // requiredVersion returns the minimum version required for the given spec -func (r requiredVersionMap) requiredVersion(spec *Spec) Version { +func (r requiredVersionMap) requiredVersion(spec *Spec) version { minVersion := vEarliest for v, isRequired := range validSpecVersions { From 6abc5e00d4c9167faccb7d9ad05c4db62ba75d8c Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Tue, 6 Aug 2024 18:01:50 +0300 Subject: [PATCH 4/7] deprecate MinimumRequiredVersion Signed-off-by: Ed Bartosh --- pkg/cdi/spec.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index c0e04321..6d721796 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -201,6 +201,12 @@ func (s *Spec) edits() *ContainerEdits { return &ContainerEdits{&s.ContainerEdits} } +// MinimumRequiredVersion determines the minimum spec version for the input spec. +// Deprecated: use cdi.MinimumRequiredVersion instead +func MinimumRequiredVersion(spec *cdi.Spec) (string, error) { + return cdi.MinimumRequiredVersion(spec) +} + // Validate the Spec. func (s *Spec) validate() (map[string]*Device, error) { if err := cdi.ValidateVersion(s.Version); err != nil { From 54e62a85a167cd06c562d1b3ee264275e3f78d83 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Tue, 6 Aug 2024 18:57:39 +0300 Subject: [PATCH 5/7] move minimal version check to spces-go/ValidateVersion Signed-off-by: Ed Bartosh Co-authored-by: Evan Lezar --- pkg/cdi/spec.go | 11 +---------- pkg/cdi/spec_test.go | 2 +- specs-go/version.go | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index 6d721796..e19e7828 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -209,18 +209,9 @@ func MinimumRequiredVersion(spec *cdi.Spec) (string, error) { // Validate the Spec. func (s *Spec) validate() (map[string]*Device, error) { - if err := cdi.ValidateVersion(s.Version); err != nil { + if err := cdi.ValidateVersion(s.Spec); err != nil { return nil, err } - - minVersion, err := cdi.MinimumRequiredVersion(s.Spec) - if err != nil { - return nil, fmt.Errorf("could not determine minimum required version: %v", err) - } - if cdi.NewVersion(minVersion).IsGreaterThan(cdi.NewVersion(s.Version)) { - return nil, fmt.Errorf("the spec version must be at least v%v", minVersion) - } - if err := ValidateVendorName(s.vendor); err != nil { return nil, err } diff --git a/pkg/cdi/spec_test.go b/pkg/cdi/spec_test.go index df7ff683..649d2237 100644 --- a/pkg/cdi/spec_test.go +++ b/pkg/cdi/spec_test.go @@ -525,7 +525,7 @@ func specType(content []byte) string { } func TestCurrentVersionIsValid(t *testing.T) { - require.NoError(t, cdi.ValidateVersion(cdi.CurrentVersion)) + require.NoError(t, cdi.ValidateVersion(&cdi.Spec{Version: cdi.CurrentVersion})) } func TestRequiredVersion(t *testing.T) { diff --git a/specs-go/version.go b/specs-go/version.go index 05c72dde..9feebca7 100644 --- a/specs-go/version.go +++ b/specs-go/version.go @@ -58,10 +58,20 @@ var validSpecVersions = requiredVersionMap{ v080: requiresV080, } -// ValidateVersion checks whether the specified spec version is supported. -func ValidateVersion(version string) error { - if !validSpecVersions.isValidVersion(version) { - return fmt.Errorf("invalid version %q", version) +// ValidateVersion checks whether the specified spec version is valid. +// In addition to checking whether the spec version is in the set of known versions, +// the spec is inspected to determine whether the features used are available in specified +// version. +func ValidateVersion(spec *Spec) error { + if !validSpecVersions.isValidVersion(spec.Version) { + return fmt.Errorf("invalid version %q", spec.Version) + } + minVersion, err := MinimumRequiredVersion(spec) + if err != nil { + return fmt.Errorf("could not determine minimum required version: %w", err) + } + if newVersion(minVersion).IsGreaterThan(newVersion(spec.Version)) { + return fmt.Errorf("the spec version must be at least v%v", minVersion) } return nil } From 2cad149c78e22c1edf05a71af936ef9aed5f51dd Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Thu, 8 Aug 2024 14:36:08 +0300 Subject: [PATCH 6/7] Return ParseQualifier back. Will deprecate it in a separate PR Signed-off-by: Ed Bartosh --- pkg/cdi/qualified-device.go | 13 +++++++++++++ pkg/cdi/spec.go | 7 +++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/pkg/cdi/qualified-device.go b/pkg/cdi/qualified-device.go index 49099c8f..0bdfdc16 100644 --- a/pkg/cdi/qualified-device.go +++ b/pkg/cdi/qualified-device.go @@ -66,6 +66,19 @@ func ParseDevice(device string) (string, string, string) { return parser.ParseDevice(device) } +// ParseQualifier splits a device qualifier into vendor and class. +// The syntax for a device qualifier is +// +// "/" +// +// If parsing fails, an empty vendor and the class set to the +// verbatim input is returned. +// +// Deprecated: use parser.ParseQualifier instead +func ParseQualifier(kind string) (string, string) { + return parser.ParseQualifier(kind) +} + // ValidateVendorName checks the validity of a vendor name. // A vendor name may contain the following ASCII characters: // - upper- and lowercase letters ('A'-'Z', 'a'-'z') diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index e19e7828..6ee986ec 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -28,7 +28,6 @@ import ( "sigs.k8s.io/yaml" "tags.cncf.io/container-device-interface/internal/validation" - "tags.cncf.io/container-device-interface/pkg/parser" cdi "tags.cncf.io/container-device-interface/specs-go" ) @@ -106,7 +105,7 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { spec.path += defaultSpecExt } - spec.vendor, spec.class = parser.ParseQualifier(spec.Kind) + spec.vendor, spec.class = ParseQualifier(spec.Kind) if spec.devices, err = spec.validate(); err != nil { return nil, fmt.Errorf("invalid CDI Spec: %w", err) @@ -317,7 +316,7 @@ func GenerateTransientSpecName(vendor, class, transientID string) string { // the Spec does not contain a valid vendor or class, it returns // an empty name and a non-nil error. func GenerateNameForSpec(raw *cdi.Spec) (string, error) { - vendor, class := parser.ParseQualifier(raw.Kind) + vendor, class := ParseQualifier(raw.Kind) if vendor == "" { return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind) } @@ -331,7 +330,7 @@ func GenerateNameForSpec(raw *cdi.Spec) (string, error) { // If the Spec does not contain a valid vendor or class, it returns an // an empty name and a non-nil error. func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) { - vendor, class := parser.ParseQualifier(raw.Kind) + vendor, class := ParseQualifier(raw.Kind) if vendor == "" { return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind) } From 7dd001f07b0262ee4aa90dc724b6a1223690a299 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Thu, 8 Aug 2024 15:20:46 +0300 Subject: [PATCH 7/7] Make all unrelated APIs private Signed-off-by: Ed Bartosh --- pkg/parser/parser.go | 10 ++++++---- specs-go/parser.go | 8 ++++---- specs-go/version.go | 18 +++++++++--------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index d307ff55..53259895 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -19,8 +19,6 @@ package parser import ( "fmt" "strings" - - specs "tags.cncf.io/container-device-interface/specs-go" ) // QualifiedName returns the qualified name for a device. @@ -107,7 +105,11 @@ func ParseDevice(device string) (string, string, string) { // If parsing fails, an empty vendor and the class set to the // verbatim input is returned. func ParseQualifier(kind string) (string, string) { - return specs.ParseQualifier(kind) + parts := strings.SplitN(kind, "/", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", kind + } + return parts[0], parts[1] } // ValidateVendorName checks the validity of a vendor name. @@ -196,7 +198,7 @@ func ValidateDeviceName(name string) error { // IsLetter reports whether the rune is a letter. func IsLetter(c rune) bool { - return specs.IsLetter(c) + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') } // IsDigit reports whether the rune is a digit. diff --git a/specs-go/parser.go b/specs-go/parser.go index 7bdf0ea4..f6c07da4 100644 --- a/specs-go/parser.go +++ b/specs-go/parser.go @@ -18,14 +18,14 @@ package specs import "strings" -// ParseQualifier splits a device qualifier into vendor and class. +// parseQualifier splits a device qualifier into vendor and class. // The syntax for a device qualifier is // // "/" // // If parsing fails, an empty vendor and the class set to the // verbatim input is returned. -func ParseQualifier(kind string) (string, string) { +func parseQualifier(kind string) (string, string) { parts := strings.SplitN(kind, "/", 2) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { return "", kind @@ -33,7 +33,7 @@ func ParseQualifier(kind string) (string, string) { return parts[0], parts[1] } -// IsLetter reports whether the rune is a letter. -func IsLetter(c rune) bool { +// isLetter reports whether the rune is an ASCII letter. +func isLetter(c rune) bool { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') } diff --git a/specs-go/version.go b/specs-go/version.go index 9feebca7..3c1f2919 100644 --- a/specs-go/version.go +++ b/specs-go/version.go @@ -70,7 +70,7 @@ func ValidateVersion(spec *Spec) error { if err != nil { return fmt.Errorf("could not determine minimum required version: %w", err) } - if newVersion(minVersion).IsGreaterThan(newVersion(spec.Version)) { + if newVersion(minVersion).isGreaterThan(newVersion(spec.Version)) { return fmt.Errorf("the spec version must be at least v%v", minVersion) } return nil @@ -96,13 +96,13 @@ func (v version) String() string { return strings.TrimPrefix(string(v), "v") } -// IsGreaterThan checks with a version is greater than the specified version. -func (v version) IsGreaterThan(o version) bool { +// isGreaterThan checks with a version is greater than the specified version. +func (v version) isGreaterThan(o version) bool { return semver.Compare(string(v), string(o)) > 0 } -// IsLatest checks whether the version is the latest supported version -func (v version) IsLatest() bool { +// isLatest checks whether the version is the latest supported version +func (v version) isLatest() bool { return v == vCurrent } @@ -126,11 +126,11 @@ func (r requiredVersionMap) requiredVersion(spec *Spec) version { if isRequired == nil { continue } - if isRequired(spec) && v.IsGreaterThan(minVersion) { + if isRequired(spec) && v.isGreaterThan(minVersion) { minVersion = v } // If we have already detected the latest version then no later version could be detected - if minVersion.IsLatest() { + if minVersion.isLatest() { break } } @@ -183,7 +183,7 @@ func requiresV060(spec *Spec) bool { } // The v0.6.0 spec allows dots "." in Kind name label (class) - vendor, class := ParseQualifier(spec.Kind) + vendor, class := parseQualifier(spec.Kind) if vendor != "" { if strings.ContainsRune(class, '.') { return true @@ -199,7 +199,7 @@ func requiresV050(spec *Spec) bool { for _, d := range spec.Devices { // The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter - if len(d.Name) > 0 && !IsLetter(rune(d.Name[0])) { + if len(d.Name) > 0 && !isLetter(rune(d.Name[0])) { return true } edits = append(edits, &d.ContainerEdits)