mirror of
				https://github.com/NVIDIA/nvidia-container-toolkit
				synced 2025-06-26 18:18:24 +00:00 
			
		
		
		
	Merge branch 'bump-dependencies' into 'main'
Bump dependencies See merge request nvidia/container-toolkit/container-toolkit!444
This commit is contained in:
		
						commit
						032982ab9c
					
				| @ -17,14 +17,29 @@ ARG CUDA_VERSION | ||||
| ARG GOLANG_VERSION=x.x.x | ||||
| ARG VERSION="N/A" | ||||
| 
 | ||||
| # NOTE: In cases where the libc version is a concern, we would have to use an | ||||
| # image based on the target OS to build the golang executables here -- especially | ||||
| # if cgo code is included. | ||||
| FROM golang:${GOLANG_VERSION} as build | ||||
| FROM nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST} as build | ||||
| 
 | ||||
| # We override the GOPATH to ensure that the binaries are installed to | ||||
| # /artifacts/bin | ||||
| ARG GOPATH=/artifacts | ||||
| RUN yum install -y \ | ||||
|     wget make git gcc \ | ||||
|      && \ | ||||
|     rm -rf /var/cache/yum/* | ||||
| 
 | ||||
| ARG GOLANG_VERSION=x.x.x | ||||
| RUN set -eux; \ | ||||
|     \ | ||||
|     arch="$(uname -m)"; \ | ||||
|     case "${arch##*-}" in \ | ||||
|         x86_64 | amd64) ARCH='amd64' ;; \ | ||||
|         ppc64el | ppc64le) ARCH='ppc64le' ;; \ | ||||
|         aarch64) ARCH='arm64' ;; \ | ||||
|         *) echo "unsupported architecture" ; exit 1 ;; \ | ||||
|     esac; \ | ||||
|     wget -nv -O - https://storage.googleapis.com/golang/go${GOLANG_VERSION}.linux-${ARCH}.tar.gz \ | ||||
|     | tar -C /usr/local -xz | ||||
| 
 | ||||
| 
 | ||||
| ENV GOPATH /go | ||||
| ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH | ||||
| 
 | ||||
| WORKDIR /build | ||||
| COPY . . | ||||
|  | ||||
| @ -17,14 +17,28 @@ ARG CUDA_VERSION | ||||
| ARG GOLANG_VERSION=x.x.x | ||||
| ARG VERSION="N/A" | ||||
| 
 | ||||
| # NOTE: In cases where the libc version is a concern, we would have to use an | ||||
| # image based on the target OS to build the golang executables here -- especially | ||||
| # if cgo code is included. | ||||
| FROM golang:${GOLANG_VERSION} as build | ||||
| FROM nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST} as build | ||||
| 
 | ||||
| # We override the GOPATH to ensure that the binaries are installed to | ||||
| # /artifacts/bin | ||||
| ARG GOPATH=/artifacts | ||||
| RUN apt-get update && \ | ||||
|     apt-get install -y wget make git gcc \ | ||||
|     && \ | ||||
|     rm -rf /var/lib/apt/lists/* | ||||
| 
 | ||||
| ARG GOLANG_VERSION=x.x.x | ||||
| RUN set -eux; \ | ||||
|     \ | ||||
|     arch="$(uname -m)"; \ | ||||
|     case "${arch##*-}" in \ | ||||
|         x86_64 | amd64) ARCH='amd64' ;; \ | ||||
|         ppc64el | ppc64le) ARCH='ppc64le' ;; \ | ||||
|         aarch64) ARCH='arm64' ;; \ | ||||
|         *) echo "unsupported architecture" ; exit 1 ;; \ | ||||
|     esac; \ | ||||
|     wget -nv -O - https://storage.googleapis.com/golang/go${GOLANG_VERSION}.linux-${ARCH}.tar.gz \ | ||||
|     | tar -C /usr/local -xz | ||||
| 
 | ||||
| ENV GOPATH /go | ||||
| ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH | ||||
| 
 | ||||
| WORKDIR /build | ||||
| COPY . . | ||||
|  | ||||
							
								
								
									
										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 | ||||
|  | ||||
| @ -31,7 +31,7 @@ NVIDIA_CONTAINER_RUNTIME_VERSION := 3.14.0 | ||||
| LIBNVIDIA_CONTAINER0_VERSION := 0.10.0+jetpack | ||||
| 
 | ||||
| CUDA_VERSION := 12.2.0 | ||||
| GOLANG_VERSION := 1.20.3 | ||||
| GOLANG_VERSION := 1.20.5 | ||||
| 
 | ||||
| GIT_COMMIT ?= $(shell git describe --match="" --dirty --long --always --abbrev=40 2> /dev/null || echo "") | ||||
| GIT_COMMIT_SHORT ?= $(shell git rev-parse --short HEAD 2> /dev/null || echo "") | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user