go.mod: github.com/.../container-device-interface v0.6.0
https://github.com/container-orchestrated-devices/container-device-interface/compare/v0.5.4...v0.6.0 Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
		@@ -29,6 +29,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
 | 
			
		||||
	"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	"github.com/containerd/containerd/cmd/ctr/commands"
 | 
			
		||||
	"github.com/containerd/containerd/containers"
 | 
			
		||||
@@ -355,7 +356,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
 | 
			
		||||
		}
 | 
			
		||||
		var cdiDeviceIDs []string
 | 
			
		||||
		for _, dev := range context.StringSlice("device") {
 | 
			
		||||
			if cdi.IsQualifiedName(dev) {
 | 
			
		||||
			if parser.IsQualifiedName(dev) {
 | 
			
		||||
				cdiDeviceIDs = append(cdiDeviceIDs, dev)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@@ -7,7 +7,7 @@ require (
 | 
			
		||||
	github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652
 | 
			
		||||
	github.com/Microsoft/go-winio v0.6.1
 | 
			
		||||
	github.com/Microsoft/hcsshim v0.10.0-rc.9
 | 
			
		||||
	github.com/container-orchestrated-devices/container-device-interface v0.5.4
 | 
			
		||||
	github.com/container-orchestrated-devices/container-device-interface v0.6.0
 | 
			
		||||
	github.com/containerd/btrfs/v2 v2.0.0
 | 
			
		||||
	github.com/containerd/cgroups/v3 v3.0.2
 | 
			
		||||
	github.com/containerd/console v1.0.3
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							@@ -176,8 +176,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
 | 
			
		||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
 | 
			
		||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
 | 
			
		||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 | 
			
		||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4 h1:PqQGqJqQttMP5oJ/qNGEg8JttlHqGY3xDbbcKb5T9E8=
 | 
			
		||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4/go.mod h1:DjE95rfPiiSmG7uVXtg0z6MnPm/Lx4wxKCIts0ZE0vg=
 | 
			
		||||
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/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
 | 
			
		||||
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
 | 
			
		||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ require (
 | 
			
		||||
	github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect
 | 
			
		||||
	github.com/Microsoft/go-winio v0.6.1 // indirect
 | 
			
		||||
	github.com/cilium/ebpf v0.9.1 // indirect
 | 
			
		||||
	github.com/container-orchestrated-devices/container-device-interface v0.5.4 // indirect
 | 
			
		||||
	github.com/container-orchestrated-devices/container-device-interface v0.6.0 // indirect
 | 
			
		||||
	github.com/containerd/cgroups v1.1.0 // indirect
 | 
			
		||||
	github.com/containerd/console v1.0.3 // indirect
 | 
			
		||||
	github.com/containerd/fifo v1.1.0 // indirect
 | 
			
		||||
 
 | 
			
		||||
@@ -710,8 +710,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH
 | 
			
		||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
 | 
			
		||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
 | 
			
		||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 | 
			
		||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4 h1:PqQGqJqQttMP5oJ/qNGEg8JttlHqGY3xDbbcKb5T9E8=
 | 
			
		||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4/go.mod h1:DjE95rfPiiSmG7uVXtg0z6MnPm/Lx4wxKCIts0ZE0vg=
 | 
			
		||||
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/containerd/btrfs/v2 v2.0.0/go.mod h1:swkD/7j9HApWpzl8OHfrHNxppPd9l44DFZdF94BUj9k=
 | 
			
		||||
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
 | 
			
		||||
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
 | 
			
		||||
@@ -1273,6 +1273,7 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.1.0-rc.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
									
									
								
							@@ -79,10 +79,13 @@ github.com/cilium/ebpf/internal
 | 
			
		||||
github.com/cilium/ebpf/internal/sys
 | 
			
		||||
github.com/cilium/ebpf/internal/unix
 | 
			
		||||
github.com/cilium/ebpf/link
 | 
			
		||||
# github.com/container-orchestrated-devices/container-device-interface v0.5.4
 | 
			
		||||
# 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/containerd/btrfs/v2 v2.0.0
 | 
			
		||||
## explicit; go 1.19
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user