mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-03-25 23:19:54 +00:00
Update container-device-interface to v0.6.0
Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
parent
c98f6ea395
commit
96aeb9bf64
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.20
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1
|
||||
github.com/NVIDIA/go-nvml v0.12.0-1
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4-0.20230111111500-5b3b5d81179a
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.6.0
|
||||
github.com/fsnotify/fsnotify v1.5.4
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.2
|
||||
github.com/pelletier/go-toml v1.9.4
|
||||
|
2
go.sum
2
go.sum
@ -7,6 +7,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4-0.20230111111500-5b3b5d81179a h1:sP3PcgyIkRlHqfF3Jfpe/7G8kf/qpzG4C8r94y9hLbE=
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4-0.20230111111500-5b3b5d81179a/go.mod h1:xMRa4fJgXzSDFUCURSimOUgoSc+odohvO3uXT9xjqH0=
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.6.0 h1:aWwcz/Ep0Fd7ZuBjQGjU/jdPloM7ydhMW13h85jZNvk=
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.6.0/go.mod h1:OQlgtJtDrOxSQ1BWODC8OZK1tzi9W69wek+Jy17ndzo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
|
57
vendor/github.com/container-orchestrated-devices/container-device-interface/internal/validation/k8s/objectmeta.go
generated
vendored
Normal file
57
vendor/github.com/container-orchestrated-devices/container-device-interface/internal/validation/k8s/objectmeta.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes 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.
|
||||
*/
|
||||
|
||||
// Adapted from k8s.io/apimachinery/pkg/api/validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/7687996c715ee7d5c8cf1e3215e607eb065a4221/pkg/api/validation/objectmeta.go
|
||||
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/internal/multierror"
|
||||
)
|
||||
|
||||
// TotalAnnotationSizeLimitB defines the maximum size of all annotations in characters.
|
||||
const TotalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
|
||||
|
||||
// ValidateAnnotations validates that a set of annotations are correctly defined.
|
||||
func ValidateAnnotations(annotations map[string]string, path string) error {
|
||||
errors := multierror.New()
|
||||
for k := range annotations {
|
||||
// The rule is QualifiedName except that case doesn't matter, so convert to lowercase before checking.
|
||||
for _, msg := range IsQualifiedName(strings.ToLower(k)) {
|
||||
errors = multierror.Append(errors, fmt.Errorf("%v.%v is invalid: %v", path, k, msg))
|
||||
}
|
||||
}
|
||||
if err := ValidateAnnotationsSize(annotations); err != nil {
|
||||
errors = multierror.Append(errors, fmt.Errorf("%v is too long: %v", path, err))
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// ValidateAnnotationsSize validates that a set of annotations is not too large.
|
||||
func ValidateAnnotationsSize(annotations map[string]string) error {
|
||||
var totalSize int64
|
||||
for k, v := range annotations {
|
||||
totalSize += (int64)(len(k)) + (int64)(len(v))
|
||||
}
|
||||
if totalSize > (int64)(TotalAnnotationSizeLimitB) {
|
||||
return fmt.Errorf("annotations size %d is larger than limit %d", totalSize, TotalAnnotationSizeLimitB)
|
||||
}
|
||||
return nil
|
||||
}
|
217
vendor/github.com/container-orchestrated-devices/container-device-interface/internal/validation/k8s/validation.go
generated
vendored
Normal file
217
vendor/github.com/container-orchestrated-devices/container-device-interface/internal/validation/k8s/validation.go
generated
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes 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.
|
||||
*/
|
||||
|
||||
// Adapted from k8s.io/apimachinery/pkg/util/validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/7687996c715ee7d5c8cf1e3215e607eb065a4221/pkg/util/validation/validation.go
|
||||
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const qnameCharFmt string = "[A-Za-z0-9]"
|
||||
const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
|
||||
const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
|
||||
const qualifiedNameErrMsg string = "must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
|
||||
const qualifiedNameMaxLength int = 63
|
||||
|
||||
var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")
|
||||
|
||||
// IsQualifiedName tests whether the value passed is what Kubernetes calls a
|
||||
// "qualified name". This is a format used in various places throughout the
|
||||
// system. If the value is not valid, a list of error strings is returned.
|
||||
// Otherwise an empty list (or nil) is returned.
|
||||
func IsQualifiedName(value string) []string {
|
||||
var errs []string
|
||||
parts := strings.Split(value, "/")
|
||||
var name string
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
name = parts[0]
|
||||
case 2:
|
||||
var prefix string
|
||||
prefix, name = parts[0], parts[1]
|
||||
if len(prefix) == 0 {
|
||||
errs = append(errs, "prefix part "+EmptyError())
|
||||
} else if msgs := IsDNS1123Subdomain(prefix); len(msgs) != 0 {
|
||||
errs = append(errs, prefixEach(msgs, "prefix part ")...)
|
||||
}
|
||||
default:
|
||||
return append(errs, "a qualified name "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc")+
|
||||
" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')")
|
||||
}
|
||||
|
||||
if len(name) == 0 {
|
||||
errs = append(errs, "name part "+EmptyError())
|
||||
} else if len(name) > qualifiedNameMaxLength {
|
||||
errs = append(errs, "name part "+MaxLenError(qualifiedNameMaxLength))
|
||||
}
|
||||
if !qualifiedNameRegexp.MatchString(name) {
|
||||
errs = append(errs, "name part "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc"))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
|
||||
const labelValueErrMsg string = "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
|
||||
|
||||
// LabelValueMaxLength is a label's max length
|
||||
const LabelValueMaxLength int = 63
|
||||
|
||||
var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$")
|
||||
|
||||
// IsValidLabelValue tests whether the value passed is a valid label value. If
|
||||
// the value is not valid, a list of error strings is returned. Otherwise an
|
||||
// empty list (or nil) is returned.
|
||||
func IsValidLabelValue(value string) []string {
|
||||
var errs []string
|
||||
if len(value) > LabelValueMaxLength {
|
||||
errs = append(errs, MaxLenError(LabelValueMaxLength))
|
||||
}
|
||||
if !labelValueRegexp.MatchString(value) {
|
||||
errs = append(errs, RegexError(labelValueErrMsg, labelValueFmt, "MyValue", "my_value", "12345"))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
|
||||
const dns1123LabelErrMsg string = "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character"
|
||||
|
||||
// DNS1123LabelMaxLength is a label's max length in DNS (RFC 1123)
|
||||
const DNS1123LabelMaxLength int = 63
|
||||
|
||||
var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")
|
||||
|
||||
// IsDNS1123Label tests for a string that conforms to the definition of a label in
|
||||
// DNS (RFC 1123).
|
||||
func IsDNS1123Label(value string) []string {
|
||||
var errs []string
|
||||
if len(value) > DNS1123LabelMaxLength {
|
||||
errs = append(errs, MaxLenError(DNS1123LabelMaxLength))
|
||||
}
|
||||
if !dns1123LabelRegexp.MatchString(value) {
|
||||
errs = append(errs, RegexError(dns1123LabelErrMsg, dns1123LabelFmt, "my-name", "123-abc"))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
|
||||
const dns1123SubdomainErrorMsg string = "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character"
|
||||
|
||||
// DNS1123SubdomainMaxLength is a subdomain's max length in DNS (RFC 1123)
|
||||
const DNS1123SubdomainMaxLength int = 253
|
||||
|
||||
var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")
|
||||
|
||||
// IsDNS1123Subdomain tests for a string that conforms to the definition of a
|
||||
// subdomain in DNS (RFC 1123).
|
||||
func IsDNS1123Subdomain(value string) []string {
|
||||
var errs []string
|
||||
if len(value) > DNS1123SubdomainMaxLength {
|
||||
errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
|
||||
}
|
||||
if !dns1123SubdomainRegexp.MatchString(value) {
|
||||
errs = append(errs, RegexError(dns1123SubdomainErrorMsg, dns1123SubdomainFmt, "example.com"))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
|
||||
const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character"
|
||||
|
||||
// DNS1035LabelMaxLength is a label's max length in DNS (RFC 1035)
|
||||
const DNS1035LabelMaxLength int = 63
|
||||
|
||||
var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$")
|
||||
|
||||
// IsDNS1035Label tests for a string that conforms to the definition of a label in
|
||||
// DNS (RFC 1035).
|
||||
func IsDNS1035Label(value string) []string {
|
||||
var errs []string
|
||||
if len(value) > DNS1035LabelMaxLength {
|
||||
errs = append(errs, MaxLenError(DNS1035LabelMaxLength))
|
||||
}
|
||||
if !dns1035LabelRegexp.MatchString(value) {
|
||||
errs = append(errs, RegexError(dns1035LabelErrMsg, dns1035LabelFmt, "my-name", "abc-123"))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// wildcard definition - RFC 1034 section 4.3.3.
|
||||
// examples:
|
||||
// - valid: *.bar.com, *.foo.bar.com
|
||||
// - invalid: *.*.bar.com, *.foo.*.com, *bar.com, f*.bar.com, *
|
||||
const wildcardDNS1123SubdomainFmt = "\\*\\." + dns1123SubdomainFmt
|
||||
const wildcardDNS1123SubdomainErrMsg = "a wildcard DNS-1123 subdomain must start with '*.', followed by a valid DNS subdomain, which must consist of lower case alphanumeric characters, '-' or '.' and end with an alphanumeric character"
|
||||
|
||||
// IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a
|
||||
// wildcard subdomain in DNS (RFC 1034 section 4.3.3).
|
||||
func IsWildcardDNS1123Subdomain(value string) []string {
|
||||
wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^" + wildcardDNS1123SubdomainFmt + "$")
|
||||
|
||||
var errs []string
|
||||
if len(value) > DNS1123SubdomainMaxLength {
|
||||
errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
|
||||
}
|
||||
if !wildcardDNS1123SubdomainRegexp.MatchString(value) {
|
||||
errs = append(errs, RegexError(wildcardDNS1123SubdomainErrMsg, wildcardDNS1123SubdomainFmt, "*.example.com"))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// MaxLenError returns a string explanation of a "string too long" validation
|
||||
// failure.
|
||||
func MaxLenError(length int) string {
|
||||
return fmt.Sprintf("must be no more than %d characters", length)
|
||||
}
|
||||
|
||||
// RegexError returns a string explanation of a regex validation failure.
|
||||
func RegexError(msg string, fmt string, examples ...string) string {
|
||||
if len(examples) == 0 {
|
||||
return msg + " (regex used for validation is '" + fmt + "')"
|
||||
}
|
||||
msg += " (e.g. "
|
||||
for i := range examples {
|
||||
if i > 0 {
|
||||
msg += " or "
|
||||
}
|
||||
msg += "'" + examples[i] + "', "
|
||||
}
|
||||
msg += "regex used for validation is '" + fmt + "')"
|
||||
return msg
|
||||
}
|
||||
|
||||
// EmptyError returns a string explanation of a "must not be empty" validation
|
||||
// failure.
|
||||
func EmptyError() string {
|
||||
return "must be non-empty"
|
||||
}
|
||||
|
||||
func prefixEach(msgs []string, prefix string) []string {
|
||||
for i := range msgs {
|
||||
msgs[i] = prefix + msgs[i]
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
// InclusiveRangeError returns a string explanation of a numeric "must be
|
||||
// between" validation failure.
|
||||
func InclusiveRangeError(lo, hi int) string {
|
||||
return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
|
||||
}
|
56
vendor/github.com/container-orchestrated-devices/container-device-interface/internal/validation/validate.go
generated
vendored
Normal file
56
vendor/github.com/container-orchestrated-devices/container-device-interface/internal/validation/validate.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/internal/validation/k8s"
|
||||
)
|
||||
|
||||
// ValidateSpecAnnotations checks whether spec annotations are valid.
|
||||
func ValidateSpecAnnotations(name string, any interface{}) error {
|
||||
if any == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := any.(type) {
|
||||
case map[string]interface{}:
|
||||
annotations := make(map[string]string)
|
||||
for k, v := range v {
|
||||
if s, ok := v.(string); ok {
|
||||
annotations[k] = s
|
||||
} else {
|
||||
return fmt.Errorf("invalid annotation %v.%v; %v is not a string", name, k, any)
|
||||
}
|
||||
}
|
||||
return validateSpecAnnotations(name, annotations)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSpecAnnotations checks whether spec annotations are valid.
|
||||
func validateSpecAnnotations(name string, annotations map[string]string) error {
|
||||
path := "annotations"
|
||||
if name != "" {
|
||||
path = strings.Join([]string{name, path}, ".")
|
||||
}
|
||||
|
||||
return k8s.ValidateAnnotations(annotations, path)
|
||||
}
|
@ -20,6 +20,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -101,22 +103,22 @@ func AnnotationKey(pluginName, deviceID string) (string, error) {
|
||||
return "", fmt.Errorf("invalid plugin+deviceID %q, too long", name)
|
||||
}
|
||||
|
||||
if c := rune(name[0]); !isAlphaNumeric(c) {
|
||||
if c := rune(name[0]); !parser.IsAlphaNumeric(c) {
|
||||
return "", fmt.Errorf("invalid name %q, first '%c' should be alphanumeric",
|
||||
name, c)
|
||||
}
|
||||
if len(name) > 2 {
|
||||
for _, c := range name[1 : len(name)-1] {
|
||||
switch {
|
||||
case isAlphaNumeric(c):
|
||||
case parser.IsAlphaNumeric(c):
|
||||
case c == '_' || c == '-' || c == '.':
|
||||
default:
|
||||
return "", fmt.Errorf("invalid name %q, invalid charcter '%c'",
|
||||
return "", fmt.Errorf("invalid name %q, invalid character '%c'",
|
||||
name, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
if c := rune(name[len(name)-1]); !isAlphaNumeric(c) {
|
||||
if c := rune(name[len(name)-1]); !parser.IsAlphaNumeric(c) {
|
||||
return "", fmt.Errorf("invalid name %q, last '%c' should be alphanumeric",
|
||||
name, c)
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ func (d *DeviceNode) Validate() error {
|
||||
}
|
||||
for _, bit := range d.Permissions {
|
||||
if bit != 'r' && bit != 'w' && bit != 'm' {
|
||||
return fmt.Errorf("device %q: invalid persmissions %q",
|
||||
return fmt.Errorf("device %q: invalid permissions %q",
|
||||
d.Path, d.Permissions)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package cdi
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/internal/validation"
|
||||
"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
|
||||
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
@ -50,7 +52,7 @@ func (d *Device) GetSpec() *Spec {
|
||||
|
||||
// GetQualifiedName returns the qualified name for this device.
|
||||
func (d *Device) GetQualifiedName() string {
|
||||
return QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), d.Name)
|
||||
return parser.QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), d.Name)
|
||||
}
|
||||
|
||||
// ApplyEdits applies the device-speific container edits to an OCI Spec.
|
||||
@ -68,6 +70,13 @@ func (d *Device) validate() error {
|
||||
if err := ValidateDeviceName(d.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
name := d.Name
|
||||
if d.spec != nil {
|
||||
name = d.GetQualifiedName()
|
||||
}
|
||||
if err := validation.ValidateSpecAnnotations(name, d.Annotations); err != nil {
|
||||
return err
|
||||
}
|
||||
edits := d.edits()
|
||||
if edits.isEmpty() {
|
||||
return fmt.Errorf("invalid device, empty device edits")
|
||||
|
@ -137,7 +137,7 @@
|
||||
// were loaded from. The later a directory occurs in the list of CDI
|
||||
// directories to scan, the higher priority Spec files loaded from that
|
||||
// directory are assigned to. When two or more Spec files define the
|
||||
// same device, conflict is resolved by chosing the definition from the
|
||||
// same device, conflict is resolved by choosing the definition from the
|
||||
// Spec file with the highest priority.
|
||||
//
|
||||
// The default CDI directory configuration is chosen to encourage
|
||||
@ -197,7 +197,7 @@
|
||||
// return registry.SpecDB().WriteSpec(spec, specName)
|
||||
// }
|
||||
//
|
||||
// Similary, generating and later cleaning up transient Spec files can be
|
||||
// Similarly, generating and later cleaning up transient Spec files can be
|
||||
// done with code fragments similar to the following. These transient Spec
|
||||
// files are temporary Spec files with container-specific parametrization.
|
||||
// They are typically created before the associated container is created
|
||||
|
@ -17,27 +17,32 @@
|
||||
package cdi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
|
||||
)
|
||||
|
||||
// QualifiedName returns the qualified name for a device.
|
||||
// The syntax for a qualified device names is
|
||||
// "<vendor>/<class>=<name>".
|
||||
// A valid vendor 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', '-', '_', '.', ':'
|
||||
//
|
||||
// "<vendor>/<class>=<name>".
|
||||
//
|
||||
// A valid vendor and class name may contain the following runes:
|
||||
//
|
||||
// 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
|
||||
//
|
||||
// A valid device name may contain the following runes:
|
||||
//
|
||||
// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
|
||||
//
|
||||
// Deprecated: use parser.QualifiedName instead
|
||||
func QualifiedName(vendor, class, name string) string {
|
||||
return vendor + "/" + class + "=" + name
|
||||
return parser.QualifiedName(vendor, class, name)
|
||||
}
|
||||
|
||||
// IsQualifiedName tests if a device name is qualified.
|
||||
//
|
||||
// Deprecated: use parser.IsQualifiedName instead
|
||||
func IsQualifiedName(device string) bool {
|
||||
_, _, _, err := ParseQualifiedName(device)
|
||||
return err == nil
|
||||
return parser.IsQualifiedName(device)
|
||||
}
|
||||
|
||||
// ParseQualifiedName splits a qualified name into device vendor, class,
|
||||
@ -45,66 +50,33 @@ func IsQualifiedName(device string) bool {
|
||||
// of the split components fail to pass syntax validation, vendor and
|
||||
// class are returned as empty, together with the verbatim input as the
|
||||
// name and an error describing the reason for failure.
|
||||
//
|
||||
// Deprecated: use parser.ParseQualifiedName instead
|
||||
func ParseQualifiedName(device string) (string, string, string, error) {
|
||||
vendor, class, name := ParseDevice(device)
|
||||
|
||||
if vendor == "" {
|
||||
return "", "", device, fmt.Errorf("unqualified device %q, missing vendor", device)
|
||||
}
|
||||
if class == "" {
|
||||
return "", "", device, fmt.Errorf("unqualified device %q, missing class", device)
|
||||
}
|
||||
if name == "" {
|
||||
return "", "", device, fmt.Errorf("unqualified device %q, missing device name", device)
|
||||
}
|
||||
|
||||
if err := ValidateVendorName(vendor); err != nil {
|
||||
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
|
||||
}
|
||||
if err := ValidateClassName(class); err != nil {
|
||||
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
|
||||
}
|
||||
if err := ValidateDeviceName(name); err != nil {
|
||||
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
|
||||
}
|
||||
|
||||
return vendor, class, name, nil
|
||||
return parser.ParseQualifiedName(device)
|
||||
}
|
||||
|
||||
// ParseDevice tries to split a device name into vendor, class, and name.
|
||||
// If this fails, for instance in the case of unqualified device names,
|
||||
// ParseDevice returns an empty vendor and class together with name set
|
||||
// to the verbatim input.
|
||||
//
|
||||
// Deprecated: use parser.ParseDevice instead
|
||||
func ParseDevice(device string) (string, string, string) {
|
||||
if device == "" || device[0] == '/' {
|
||||
return "", "", device
|
||||
}
|
||||
|
||||
parts := strings.SplitN(device, "=", 2)
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return "", "", device
|
||||
}
|
||||
|
||||
name := parts[1]
|
||||
vendor, class := ParseQualifier(parts[0])
|
||||
if vendor == "" {
|
||||
return "", "", device
|
||||
}
|
||||
|
||||
return vendor, class, name
|
||||
return parser.ParseDevice(device)
|
||||
}
|
||||
|
||||
// ParseQualifier splits a device qualifier into vendor and class.
|
||||
// The syntax for a device qualifier is
|
||||
// "<vendor>/<class>"
|
||||
//
|
||||
// "<vendor>/<class>"
|
||||
//
|
||||
// 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) {
|
||||
parts := strings.SplitN(kind, "/", 2)
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return "", kind
|
||||
}
|
||||
return parts[0], parts[1]
|
||||
return parser.ParseQualifier(kind)
|
||||
}
|
||||
|
||||
// ValidateVendorName checks the validity of a vendor name.
|
||||
@ -112,54 +84,21 @@ func ParseQualifier(kind string) (string, string) {
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, and dot ('_', '-', and '.')
|
||||
//
|
||||
// Deprecated: use parser.ValidateVendorName instead
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
return parser.ValidateVendorName(vendor)
|
||||
}
|
||||
|
||||
// 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 '.')
|
||||
//
|
||||
// Deprecated: use parser.ValidateClassName instead
|
||||
func ValidateClassName(class string) error {
|
||||
if class == "" {
|
||||
return fmt.Errorf("invalid (empty) device class")
|
||||
}
|
||||
if !isLetter(rune(class[0])) {
|
||||
return fmt.Errorf("invalid class %q, should start with letter", class)
|
||||
}
|
||||
for _, c := range string(class[1 : len(class)-1]) {
|
||||
switch {
|
||||
case isAlphaNumeric(c):
|
||||
case c == '_' || c == '-':
|
||||
default:
|
||||
return fmt.Errorf("invalid character '%c' in device class %q",
|
||||
c, class)
|
||||
}
|
||||
}
|
||||
if !isAlphaNumeric(rune(class[len(class)-1])) {
|
||||
return fmt.Errorf("invalid class %q, should end with a letter or digit", class)
|
||||
}
|
||||
return nil
|
||||
return parser.ValidateClassName(class)
|
||||
}
|
||||
|
||||
// ValidateDeviceName checks the validity of a device name.
|
||||
@ -167,39 +106,8 @@ func ValidateClassName(class string) error {
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, dot, colon ('_', '-', '.', ':')
|
||||
//
|
||||
// Deprecated: use parser.ValidateDeviceName instead
|
||||
func ValidateDeviceName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("invalid (empty) device name")
|
||||
}
|
||||
if !isAlphaNumeric(rune(name[0])) {
|
||||
return fmt.Errorf("invalid class %q, should start with a letter or digit", name)
|
||||
}
|
||||
if len(name) == 1 {
|
||||
return nil
|
||||
}
|
||||
for _, c := range string(name[1 : len(name)-1]) {
|
||||
switch {
|
||||
case isAlphaNumeric(c):
|
||||
case c == '_' || c == '-' || c == '.' || c == ':':
|
||||
default:
|
||||
return fmt.Errorf("invalid character '%c' in device name %q",
|
||||
c, name)
|
||||
}
|
||||
}
|
||||
if !isAlphaNumeric(rune(name[len(name)-1])) {
|
||||
return fmt.Errorf("invalid name %q, should end with a letter or digit", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLetter(c rune) bool {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
|
||||
}
|
||||
|
||||
func isDigit(c rune) bool {
|
||||
return '0' <= c && c <= '9'
|
||||
}
|
||||
|
||||
func isAlphaNumeric(c rune) bool {
|
||||
return isLetter(c) || isDigit(c)
|
||||
return parser.ValidateDeviceName(name)
|
||||
}
|
||||
|
@ -23,14 +23,12 @@ import (
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
//
|
||||
// Registry keeps a cache of all CDI Specs installed or generated on
|
||||
// the host. Registry is the primary interface clients should use to
|
||||
// interact with CDI.
|
||||
//
|
||||
// The most commonly used Registry functions are for refreshing the
|
||||
// registry and injecting CDI devices into an OCI Spec.
|
||||
//
|
||||
type Registry interface {
|
||||
RegistryResolver
|
||||
RegistryRefresher
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/internal/validation"
|
||||
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
@ -131,6 +132,7 @@ func (s *Spec) write(overwrite bool) error {
|
||||
|
||||
if filepath.Ext(s.path) == ".yaml" {
|
||||
data, err = yaml.Marshal(s.Spec)
|
||||
data = append([]byte("---\n"), data...)
|
||||
} else {
|
||||
data, err = json.Marshal(s.Spec)
|
||||
}
|
||||
@ -207,7 +209,7 @@ func (s *Spec) validate() (map[string]*Device, error) {
|
||||
|
||||
minVersion, err := MinimumRequiredVersion(s.Spec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine minumum required version: %v", err)
|
||||
return nil, fmt.Errorf("could not determine minimum required version: %v", err)
|
||||
}
|
||||
if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) {
|
||||
return nil, fmt.Errorf("the spec version must be at least v%v", minVersion)
|
||||
@ -219,6 +221,9 @@ func (s *Spec) validate() (map[string]*Device, error) {
|
||||
if err := ValidateClassName(s.class); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validation.ValidateSpecAnnotations(s.Kind, s.Annotations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.edits().Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -306,7 +311,7 @@ func GenerateSpecName(vendor, class string) string {
|
||||
// match the vendor and class of the CDI Spec. transientID should be
|
||||
// unique among all CDI users on the same host that might generate
|
||||
// transient Spec files using the same vendor/class combination. If
|
||||
// the external entity to which the lifecycle of the tranient Spec
|
||||
// the external entity to which the lifecycle of the transient Spec
|
||||
// is tied to has a unique ID of its own, then this is usually a
|
||||
// good choice for transientID.
|
||||
//
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
|
||||
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
@ -37,6 +38,7 @@ const (
|
||||
v030 version = "v0.3.0"
|
||||
v040 version = "v0.4.0"
|
||||
v050 version = "v0.5.0"
|
||||
v060 version = "v0.6.0"
|
||||
|
||||
// vEarliest is the earliest supported version of the CDI specification
|
||||
vEarliest version = v030
|
||||
@ -51,9 +53,10 @@ var validSpecVersions = requiredVersionMap{
|
||||
v030: nil,
|
||||
v040: requiresV040,
|
||||
v050: requiresV050,
|
||||
v060: requiresV060,
|
||||
}
|
||||
|
||||
// MinimumRequiredVersion determines the minumum spec version for the input spec.
|
||||
// MinimumRequiredVersion determines the minimum spec version for the input spec.
|
||||
func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
|
||||
minVersion := validSpecVersions.requiredVersion(spec)
|
||||
return minVersion.String(), nil
|
||||
@ -115,13 +118,38 @@ func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
|
||||
return minVersion
|
||||
}
|
||||
|
||||
// requiresV060 returns true if the spec uses v0.6.0 features
|
||||
func requiresV060(spec *cdi.Spec) bool {
|
||||
// The v0.6.0 spec allows annotations to be specified at a spec level
|
||||
for range spec.Annotations {
|
||||
return true
|
||||
}
|
||||
|
||||
// The v0.6.0 spec allows annotations to be specified at a device level
|
||||
for _, d := range spec.Devices {
|
||||
for range d.Annotations {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// requiresV050 returns true if the spec uses v0.5.0 features
|
||||
func requiresV050(spec *cdi.Spec) bool {
|
||||
var edits []*cdi.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 && !isLetter(rune(d.Name[0])) {
|
||||
if len(d.Name) > 0 && !parser.IsLetter(rune(d.Name[0])) {
|
||||
return true
|
||||
}
|
||||
edits = append(edits, &d.ContainerEdits)
|
||||
|
212
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/parser/parser.go
generated
vendored
Normal file
212
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/parser/parser.go
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
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 parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// QualifiedName returns the qualified name for a device.
|
||||
// The syntax for a qualified device names is
|
||||
//
|
||||
// "<vendor>/<class>=<name>".
|
||||
//
|
||||
// A valid vendor and class name may contain the following runes:
|
||||
//
|
||||
// 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
|
||||
//
|
||||
// A valid device name may contain the following runes:
|
||||
//
|
||||
// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
|
||||
func QualifiedName(vendor, class, name string) string {
|
||||
return vendor + "/" + class + "=" + name
|
||||
}
|
||||
|
||||
// IsQualifiedName tests if a device name is qualified.
|
||||
func IsQualifiedName(device string) bool {
|
||||
_, _, _, err := ParseQualifiedName(device)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ParseQualifiedName splits a qualified name into device vendor, class,
|
||||
// and name. If the device fails to parse as a qualified name, or if any
|
||||
// of the split components fail to pass syntax validation, vendor and
|
||||
// class are returned as empty, together with the verbatim input as the
|
||||
// name and an error describing the reason for failure.
|
||||
func ParseQualifiedName(device string) (string, string, string, error) {
|
||||
vendor, class, name := ParseDevice(device)
|
||||
|
||||
if vendor == "" {
|
||||
return "", "", device, fmt.Errorf("unqualified device %q, missing vendor", device)
|
||||
}
|
||||
if class == "" {
|
||||
return "", "", device, fmt.Errorf("unqualified device %q, missing class", device)
|
||||
}
|
||||
if name == "" {
|
||||
return "", "", device, fmt.Errorf("unqualified device %q, missing device name", device)
|
||||
}
|
||||
|
||||
if err := ValidateVendorName(vendor); err != nil {
|
||||
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
|
||||
}
|
||||
if err := ValidateClassName(class); err != nil {
|
||||
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
|
||||
}
|
||||
if err := ValidateDeviceName(name); err != nil {
|
||||
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
|
||||
}
|
||||
|
||||
return vendor, class, name, nil
|
||||
}
|
||||
|
||||
// ParseDevice tries to split a device name into vendor, class, and name.
|
||||
// If this fails, for instance in the case of unqualified device names,
|
||||
// ParseDevice returns an empty vendor and class together with name set
|
||||
// to the verbatim input.
|
||||
func ParseDevice(device string) (string, string, string) {
|
||||
if device == "" || device[0] == '/' {
|
||||
return "", "", device
|
||||
}
|
||||
|
||||
parts := strings.SplitN(device, "=", 2)
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return "", "", device
|
||||
}
|
||||
|
||||
name := parts[1]
|
||||
vendor, class := ParseQualifier(parts[0])
|
||||
if vendor == "" {
|
||||
return "", "", device
|
||||
}
|
||||
|
||||
return vendor, class, name
|
||||
}
|
||||
|
||||
// ParseQualifier splits a device qualifier into vendor and class.
|
||||
// The syntax for a device qualifier is
|
||||
//
|
||||
// "<vendor>/<class>"
|
||||
//
|
||||
// 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]
|
||||
}
|
||||
|
||||
// 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')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, and dot ('_', '-', and '.')
|
||||
func ValidateVendorName(vendor string) error {
|
||||
err := validateVendorOrClassName(vendor)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid vendor. %w", err)
|
||||
}
|
||||
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, dash, and dot ('_', '-', and '.')
|
||||
func ValidateClassName(class string) error {
|
||||
err := validateVendorOrClassName(class)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid class. %w", err)
|
||||
}
|
||||
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")
|
||||
}
|
||||
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 == '-' || c == '.':
|
||||
default:
|
||||
return fmt.Errorf("invalid character '%c' in name %q",
|
||||
c, name)
|
||||
}
|
||||
}
|
||||
if !IsAlphaNumeric(rune(name[len(name)-1])) {
|
||||
return fmt.Errorf("%q, should end with a letter or digit", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateDeviceName checks the validity of a device name.
|
||||
// A device name may contain the following ASCII characters:
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, dot, colon ('_', '-', '.', ':')
|
||||
func ValidateDeviceName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("invalid (empty) device name")
|
||||
}
|
||||
if !IsAlphaNumeric(rune(name[0])) {
|
||||
return fmt.Errorf("invalid class %q, should start with a letter or digit", name)
|
||||
}
|
||||
if len(name) == 1 {
|
||||
return nil
|
||||
}
|
||||
for _, c := range string(name[1 : len(name)-1]) {
|
||||
switch {
|
||||
case IsAlphaNumeric(c):
|
||||
case c == '_' || c == '-' || c == '.' || c == ':':
|
||||
default:
|
||||
return fmt.Errorf("invalid character '%c' in device name %q",
|
||||
c, name)
|
||||
}
|
||||
}
|
||||
if !IsAlphaNumeric(rune(name[len(name)-1])) {
|
||||
return fmt.Errorf("invalid name %q, should end with a letter or digit", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLetter reports whether the rune is a letter.
|
||||
func IsLetter(c rune) bool {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
|
||||
}
|
||||
|
||||
// IsDigit reports whether the rune is a digit.
|
||||
func IsDigit(c rune) bool {
|
||||
return '0' <= c && c <= '9'
|
||||
}
|
||||
|
||||
// IsAlphaNumeric reports whether the rune is a letter or digit.
|
||||
func IsAlphaNumeric(c rune) bool {
|
||||
return IsLetter(c) || IsDigit(c)
|
||||
}
|
@ -3,21 +3,24 @@ package specs
|
||||
import "os"
|
||||
|
||||
// CurrentVersion is the current version of the Spec.
|
||||
const CurrentVersion = "0.5.0"
|
||||
const CurrentVersion = "0.6.0"
|
||||
|
||||
// Spec is the base configuration for CDI
|
||||
type Spec struct {
|
||||
Version string `json:"cdiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
|
||||
Devices []Device `json:"devices"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits,omitempty"`
|
||||
// Annotations add meta information per CDI spec. Note these are CDI-specific and do not affect container metadata.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
Devices []Device `json:"devices"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits,omitempty"`
|
||||
}
|
||||
|
||||
// Device is a "Device" a container runtime can add to a container
|
||||
type Device struct {
|
||||
Name string `json:"name"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits"`
|
||||
Name string `json:"name"`
|
||||
// Annotations add meta information per device. Note these are CDI-specific and do not affect container metadata.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits"`
|
||||
}
|
||||
|
||||
// ContainerEdits are edits a container runtime must make to the OCI spec to expose the device.
|
||||
|
@ -22,7 +22,7 @@ func ApplyOCIEditsForDevice(config *spec.Spec, cdi *Spec, dev string) error {
|
||||
return fmt.Errorf("CDI: device %q not found for spec %q", dev, cdi.Kind)
|
||||
}
|
||||
|
||||
// ApplyOCIEdits applies the OCI edits the CDI spec declares globablly
|
||||
// ApplyOCIEdits applies the OCI edits the CDI spec declares globally
|
||||
func ApplyOCIEdits(config *spec.Spec, cdi *Spec) error {
|
||||
return ApplyEditsToOCISpec(config, &cdi.ContainerEdits)
|
||||
}
|
||||
|
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
@ -6,10 +6,13 @@ github.com/BurntSushi/toml/internal
|
||||
## explicit; go 1.15
|
||||
github.com/NVIDIA/go-nvml/pkg/dl
|
||||
github.com/NVIDIA/go-nvml/pkg/nvml
|
||||
# github.com/container-orchestrated-devices/container-device-interface v0.5.4-0.20230111111500-5b3b5d81179a
|
||||
# github.com/container-orchestrated-devices/container-device-interface v0.6.0
|
||||
## explicit; go 1.17
|
||||
github.com/container-orchestrated-devices/container-device-interface/internal/multierror
|
||||
github.com/container-orchestrated-devices/container-device-interface/internal/validation
|
||||
github.com/container-orchestrated-devices/container-device-interface/internal/validation/k8s
|
||||
github.com/container-orchestrated-devices/container-device-interface/pkg/cdi
|
||||
github.com/container-orchestrated-devices/container-device-interface/pkg/parser
|
||||
github.com/container-orchestrated-devices/container-device-interface/specs-go
|
||||
# github.com/cpuguy83/go-md2man/v2 v2.0.2
|
||||
## explicit; go 1.11
|
||||
|
Loading…
Reference in New Issue
Block a user