mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Replace github.com/container-orchestrated-devices/container-device-interface with tags.cncf.io
This also removes the indirect dependency on runc which has a CVE. Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
201
vendor/github.com/container-orchestrated-devices/container-device-interface/LICENSE
generated
vendored
201
vendor/github.com/container-orchestrated-devices/container-device-interface/LICENSE
generated
vendored
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright © 2022 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 multierror
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// New combines several errors into a single error. Parameters that are nil are
|
||||
// ignored. If no errors are passed in or all parameters are nil, then the
|
||||
// result is also nil.
|
||||
func New(errors ...error) error {
|
||||
// Filter out nil entries.
|
||||
numErrors := 0
|
||||
for _, err := range errors {
|
||||
if err != nil {
|
||||
errors[numErrors] = err
|
||||
numErrors++
|
||||
}
|
||||
}
|
||||
if numErrors == 0 {
|
||||
return nil
|
||||
}
|
||||
return multiError(errors[0:numErrors])
|
||||
}
|
||||
|
||||
// multiError is the underlying implementation used by New.
|
||||
//
|
||||
// Beware that a null multiError is not the same as a nil error.
|
||||
type multiError []error
|
||||
|
||||
// multiError returns all individual error strings concatenated with "\n"
|
||||
func (e multiError) Error() string {
|
||||
var builder strings.Builder
|
||||
for i, err := range e {
|
||||
if i > 0 {
|
||||
_, _ = builder.WriteString("\n")
|
||||
}
|
||||
_, _ = builder.WriteString(err.Error())
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// Append returns a new multi error all errors concatenated. Errors that are
|
||||
// multi errors get flattened, nil is ignored.
|
||||
func Append(err error, errors ...error) error {
|
||||
var result multiError
|
||||
if m, ok := err.(multiError); ok {
|
||||
result = m
|
||||
} else if err != nil {
|
||||
result = append(result, err)
|
||||
}
|
||||
|
||||
for _, e := range errors {
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
if m, ok := e.(multiError); ok {
|
||||
result = append(result, m...)
|
||||
} else {
|
||||
result = append(result, e)
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021-2022 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 cdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
// AnnotationPrefix is the prefix for CDI container annotation keys.
|
||||
AnnotationPrefix = "cdi.k8s.io/"
|
||||
)
|
||||
|
||||
// UpdateAnnotations updates annotations with a plugin-specific CDI device
|
||||
// injection request for the given devices. Upon any error a non-nil error
|
||||
// is returned and annotations are left intact. By convention plugin should
|
||||
// be in the format of "vendor.device-type".
|
||||
func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, devices []string) (map[string]string, error) {
|
||||
key, err := AnnotationKey(plugin, deviceID)
|
||||
if err != nil {
|
||||
return annotations, fmt.Errorf("CDI annotation failed: %w", err)
|
||||
}
|
||||
if _, ok := annotations[key]; ok {
|
||||
return annotations, fmt.Errorf("CDI annotation failed, key %q used", key)
|
||||
}
|
||||
value, err := AnnotationValue(devices)
|
||||
if err != nil {
|
||||
return annotations, fmt.Errorf("CDI annotation failed: %w", err)
|
||||
}
|
||||
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
annotations[key] = value
|
||||
|
||||
return annotations, nil
|
||||
}
|
||||
|
||||
// ParseAnnotations parses annotations for CDI device injection requests.
|
||||
// The keys and devices from all such requests are collected into slices
|
||||
// which are returned as the result. All devices are expected to be fully
|
||||
// qualified CDI device names. If any device fails this check empty slices
|
||||
// are returned along with a non-nil error. The annotations are expected
|
||||
// to be formatted by, or in a compatible fashion to UpdateAnnotations().
|
||||
func ParseAnnotations(annotations map[string]string) ([]string, []string, error) {
|
||||
var (
|
||||
keys []string
|
||||
devices []string
|
||||
)
|
||||
|
||||
for key, value := range annotations {
|
||||
if !strings.HasPrefix(key, AnnotationPrefix) {
|
||||
continue
|
||||
}
|
||||
for _, d := range strings.Split(value, ",") {
|
||||
if !IsQualifiedName(d) {
|
||||
return nil, nil, fmt.Errorf("invalid CDI device name %q", d)
|
||||
}
|
||||
devices = append(devices, d)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
return keys, devices, nil
|
||||
}
|
||||
|
||||
// AnnotationKey returns a unique annotation key for an device allocation
|
||||
// by a K8s device plugin. pluginName should be in the format of
|
||||
// "vendor.device-type". deviceID is the ID of the device the plugin is
|
||||
// allocating. It is used to make sure that the generated key is unique
|
||||
// even if multiple allocations by a single plugin needs to be annotated.
|
||||
func AnnotationKey(pluginName, deviceID string) (string, error) {
|
||||
const maxNameLen = 63
|
||||
|
||||
if pluginName == "" {
|
||||
return "", errors.New("invalid plugin name, empty")
|
||||
}
|
||||
if deviceID == "" {
|
||||
return "", errors.New("invalid deviceID, empty")
|
||||
}
|
||||
|
||||
name := pluginName + "_" + strings.ReplaceAll(deviceID, "/", "_")
|
||||
|
||||
if len(name) > maxNameLen {
|
||||
return "", fmt.Errorf("invalid plugin+deviceID %q, too long", name)
|
||||
}
|
||||
|
||||
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 parser.IsAlphaNumeric(c):
|
||||
case c == '_' || c == '-' || c == '.':
|
||||
default:
|
||||
return "", fmt.Errorf("invalid name %q, invalid character '%c'",
|
||||
name, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
if c := rune(name[len(name)-1]); !parser.IsAlphaNumeric(c) {
|
||||
return "", fmt.Errorf("invalid name %q, last '%c' should be alphanumeric",
|
||||
name, c)
|
||||
}
|
||||
|
||||
return AnnotationPrefix + name, nil
|
||||
}
|
||||
|
||||
// AnnotationValue returns an annotation value for the given devices.
|
||||
func AnnotationValue(devices []string) (string, error) {
|
||||
value, sep := "", ""
|
||||
for _, d := range devices {
|
||||
if _, _, _, err := ParseQualifiedName(d); err != nil {
|
||||
return "", err
|
||||
}
|
||||
value += sep + d
|
||||
sep = ","
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
581
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
581
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
@@ -1,581 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/internal/multierror"
|
||||
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// Option is an option to change some aspect of default CDI behavior.
|
||||
type Option func(*Cache) error
|
||||
|
||||
// Cache stores CDI Specs loaded from Spec directories.
|
||||
type Cache struct {
|
||||
sync.Mutex
|
||||
specDirs []string
|
||||
specs map[string][]*Spec
|
||||
devices map[string]*Device
|
||||
errors map[string][]error
|
||||
dirErrors map[string]error
|
||||
|
||||
autoRefresh bool
|
||||
watch *watch
|
||||
}
|
||||
|
||||
// WithAutoRefresh returns an option to control automatic Cache refresh.
|
||||
// By default auto-refresh is enabled, the list of Spec directories are
|
||||
// monitored and the Cache is automatically refreshed whenever a change
|
||||
// is detected. This option can be used to disable this behavior when a
|
||||
// manually refreshed mode is preferable.
|
||||
func WithAutoRefresh(autoRefresh bool) Option {
|
||||
return func(c *Cache) error {
|
||||
c.autoRefresh = autoRefresh
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewCache creates a new CDI Cache. The cache is populated from a set
|
||||
// of CDI Spec directories. These can be specified using a WithSpecDirs
|
||||
// option. The default set of directories is exposed in DefaultSpecDirs.
|
||||
func NewCache(options ...Option) (*Cache, error) {
|
||||
c := &Cache{
|
||||
autoRefresh: true,
|
||||
watch: &watch{},
|
||||
}
|
||||
|
||||
WithSpecDirs(DefaultSpecDirs...)(c)
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
return c, c.configure(options...)
|
||||
}
|
||||
|
||||
// Configure applies options to the Cache. Updates and refreshes the
|
||||
// Cache if options have changed.
|
||||
func (c *Cache) Configure(options ...Option) error {
|
||||
if len(options) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
return c.configure(options...)
|
||||
}
|
||||
|
||||
// Configure the Cache. Start/stop CDI Spec directory watch, refresh
|
||||
// the Cache if necessary.
|
||||
func (c *Cache) configure(options ...Option) error {
|
||||
var err error
|
||||
|
||||
for _, o := range options {
|
||||
if err = o(c); err != nil {
|
||||
return fmt.Errorf("failed to apply cache options: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.dirErrors = make(map[string]error)
|
||||
|
||||
c.watch.stop()
|
||||
if c.autoRefresh {
|
||||
c.watch.setup(c.specDirs, c.dirErrors)
|
||||
c.watch.start(&c.Mutex, c.refresh, c.dirErrors)
|
||||
}
|
||||
c.refresh()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refresh rescans the CDI Spec directories and refreshes the Cache.
|
||||
// In manual refresh mode the cache is always refreshed. In auto-
|
||||
// refresh mode the cache is only refreshed if it is out of date.
|
||||
func (c *Cache) Refresh() error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
// force a refresh in manual mode
|
||||
if refreshed, err := c.refreshIfRequired(!c.autoRefresh); refreshed {
|
||||
return err
|
||||
}
|
||||
|
||||
// collect and return cached errors, much like refresh() does it
|
||||
var result error
|
||||
for _, errors := range c.errors {
|
||||
result = multierror.Append(result, errors...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Refresh the Cache by rescanning CDI Spec directories and files.
|
||||
func (c *Cache) refresh() error {
|
||||
var (
|
||||
specs = map[string][]*Spec{}
|
||||
devices = map[string]*Device{}
|
||||
conflicts = map[string]struct{}{}
|
||||
specErrors = map[string][]error{}
|
||||
result []error
|
||||
)
|
||||
|
||||
// collect errors per spec file path and once globally
|
||||
collectError := func(err error, paths ...string) {
|
||||
result = append(result, err)
|
||||
for _, path := range paths {
|
||||
specErrors[path] = append(specErrors[path], err)
|
||||
}
|
||||
}
|
||||
// resolve conflicts based on device Spec priority (order of precedence)
|
||||
resolveConflict := func(name string, dev *Device, old *Device) bool {
|
||||
devSpec, oldSpec := dev.GetSpec(), old.GetSpec()
|
||||
devPrio, oldPrio := devSpec.GetPriority(), oldSpec.GetPriority()
|
||||
switch {
|
||||
case devPrio > oldPrio:
|
||||
return false
|
||||
case devPrio == oldPrio:
|
||||
devPath, oldPath := devSpec.GetPath(), oldSpec.GetPath()
|
||||
collectError(fmt.Errorf("conflicting device %q (specs %q, %q)",
|
||||
name, devPath, oldPath), devPath, oldPath)
|
||||
conflicts[name] = struct{}{}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_ = scanSpecDirs(c.specDirs, func(path string, priority int, spec *Spec, err error) error {
|
||||
path = filepath.Clean(path)
|
||||
if err != nil {
|
||||
collectError(fmt.Errorf("failed to load CDI Spec %w", err), path)
|
||||
return nil
|
||||
}
|
||||
|
||||
vendor := spec.GetVendor()
|
||||
specs[vendor] = append(specs[vendor], spec)
|
||||
|
||||
for _, dev := range spec.devices {
|
||||
qualified := dev.GetQualifiedName()
|
||||
other, ok := devices[qualified]
|
||||
if ok {
|
||||
if resolveConflict(qualified, dev, other) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
devices[qualified] = dev
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
for conflict := range conflicts {
|
||||
delete(devices, conflict)
|
||||
}
|
||||
|
||||
c.specs = specs
|
||||
c.devices = devices
|
||||
c.errors = specErrors
|
||||
|
||||
return multierror.New(result...)
|
||||
}
|
||||
|
||||
// RefreshIfRequired triggers a refresh if necessary.
|
||||
func (c *Cache) refreshIfRequired(force bool) (bool, error) {
|
||||
// We need to refresh if
|
||||
// - it's forced by an explicitly call to Refresh() in manual mode
|
||||
// - a missing Spec dir appears (added to watch) in auto-refresh mode
|
||||
if force || (c.autoRefresh && c.watch.update(c.dirErrors)) {
|
||||
return true, c.refresh()
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// InjectDevices injects the given qualified devices to an OCI Spec. It
|
||||
// returns any unresolvable devices and an error if injection fails for
|
||||
// any of the devices.
|
||||
func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error) {
|
||||
var unresolved []string
|
||||
|
||||
if ociSpec == nil {
|
||||
return devices, fmt.Errorf("can't inject devices, nil OCI Spec")
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
|
||||
edits := &ContainerEdits{}
|
||||
specs := map[*Spec]struct{}{}
|
||||
|
||||
for _, device := range devices {
|
||||
d := c.devices[device]
|
||||
if d == nil {
|
||||
unresolved = append(unresolved, device)
|
||||
continue
|
||||
}
|
||||
if _, ok := specs[d.GetSpec()]; !ok {
|
||||
specs[d.GetSpec()] = struct{}{}
|
||||
edits.Append(d.GetSpec().edits())
|
||||
}
|
||||
edits.Append(d.edits())
|
||||
}
|
||||
|
||||
if unresolved != nil {
|
||||
return unresolved, fmt.Errorf("unresolvable CDI devices %s",
|
||||
strings.Join(devices, ", "))
|
||||
}
|
||||
|
||||
if err := edits.Apply(ociSpec); err != nil {
|
||||
return nil, fmt.Errorf("failed to inject devices: %w", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// highestPrioritySpecDir returns the Spec directory with highest priority
|
||||
// and its priority.
|
||||
func (c *Cache) highestPrioritySpecDir() (string, int) {
|
||||
if len(c.specDirs) == 0 {
|
||||
return "", -1
|
||||
}
|
||||
|
||||
prio := len(c.specDirs) - 1
|
||||
dir := c.specDirs[prio]
|
||||
|
||||
return dir, prio
|
||||
}
|
||||
|
||||
// WriteSpec writes a Spec file with the given content into the highest
|
||||
// priority Spec directory. If name has a "json" or "yaml" extension it
|
||||
// choses the encoding. Otherwise the default YAML encoding is used.
|
||||
func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error {
|
||||
var (
|
||||
specDir string
|
||||
path string
|
||||
prio int
|
||||
spec *Spec
|
||||
err error
|
||||
)
|
||||
|
||||
specDir, prio = c.highestPrioritySpecDir()
|
||||
if specDir == "" {
|
||||
return errors.New("no Spec directories to write to")
|
||||
}
|
||||
|
||||
path = filepath.Join(specDir, name)
|
||||
if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
|
||||
path += defaultSpecExt
|
||||
}
|
||||
|
||||
spec, err = newSpec(raw, path, prio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.write(true)
|
||||
}
|
||||
|
||||
// RemoveSpec removes a Spec with the given name from the highest
|
||||
// priority Spec directory. This function can be used to remove a
|
||||
// Spec previously written by WriteSpec(). If the file exists and
|
||||
// its removal fails RemoveSpec returns an error.
|
||||
func (c *Cache) RemoveSpec(name string) error {
|
||||
var (
|
||||
specDir string
|
||||
path string
|
||||
err error
|
||||
)
|
||||
|
||||
specDir, _ = c.highestPrioritySpecDir()
|
||||
if specDir == "" {
|
||||
return errors.New("no Spec directories to remove from")
|
||||
}
|
||||
|
||||
path = filepath.Join(specDir, name)
|
||||
if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
|
||||
path += defaultSpecExt
|
||||
}
|
||||
|
||||
err = os.Remove(path)
|
||||
if err != nil && errors.Is(err, fs.ErrNotExist) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDevice returns the cached device for the given qualified name.
|
||||
func (c *Cache) GetDevice(device string) *Device {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
|
||||
return c.devices[device]
|
||||
}
|
||||
|
||||
// ListDevices lists all cached devices by qualified name.
|
||||
func (c *Cache) ListDevices() []string {
|
||||
var devices []string
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
|
||||
for name := range c.devices {
|
||||
devices = append(devices, name)
|
||||
}
|
||||
sort.Strings(devices)
|
||||
|
||||
return devices
|
||||
}
|
||||
|
||||
// ListVendors lists all vendors known to the cache.
|
||||
func (c *Cache) ListVendors() []string {
|
||||
var vendors []string
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
|
||||
for vendor := range c.specs {
|
||||
vendors = append(vendors, vendor)
|
||||
}
|
||||
sort.Strings(vendors)
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
// ListClasses lists all device classes known to the cache.
|
||||
func (c *Cache) ListClasses() []string {
|
||||
var (
|
||||
cmap = map[string]struct{}{}
|
||||
classes []string
|
||||
)
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
|
||||
for _, specs := range c.specs {
|
||||
for _, spec := range specs {
|
||||
cmap[spec.GetClass()] = struct{}{}
|
||||
}
|
||||
}
|
||||
for class := range cmap {
|
||||
classes = append(classes, class)
|
||||
}
|
||||
sort.Strings(classes)
|
||||
|
||||
return classes
|
||||
}
|
||||
|
||||
// GetVendorSpecs returns all specs for the given vendor.
|
||||
func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
|
||||
return c.specs[vendor]
|
||||
}
|
||||
|
||||
// GetSpecErrors returns all errors encountered for the spec during the
|
||||
// last cache refresh.
|
||||
func (c *Cache) GetSpecErrors(spec *Spec) []error {
|
||||
var errors []error
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if errs, ok := c.errors[spec.GetPath()]; ok {
|
||||
errors = make([]error, len(errs))
|
||||
copy(errors, errs)
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// GetErrors returns all errors encountered during the last
|
||||
// cache refresh.
|
||||
func (c *Cache) GetErrors() map[string][]error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
errors := map[string][]error{}
|
||||
for path, errs := range c.errors {
|
||||
errors[path] = errs
|
||||
}
|
||||
for path, err := range c.dirErrors {
|
||||
errors[path] = []error{err}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// GetSpecDirectories returns the CDI Spec directories currently in use.
|
||||
func (c *Cache) GetSpecDirectories() []string {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
dirs := make([]string, len(c.specDirs))
|
||||
copy(dirs, c.specDirs)
|
||||
return dirs
|
||||
}
|
||||
|
||||
// GetSpecDirErrors returns any errors related to configured Spec directories.
|
||||
func (c *Cache) GetSpecDirErrors() map[string]error {
|
||||
if c.dirErrors == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
errors := make(map[string]error)
|
||||
for dir, err := range c.dirErrors {
|
||||
errors[dir] = err
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// Our fsnotify helper wrapper.
|
||||
type watch struct {
|
||||
watcher *fsnotify.Watcher
|
||||
tracked map[string]bool
|
||||
}
|
||||
|
||||
// Setup monitoring for the given Spec directories.
|
||||
func (w *watch) setup(dirs []string, dirErrors map[string]error) {
|
||||
var (
|
||||
dir string
|
||||
err error
|
||||
)
|
||||
w.tracked = make(map[string]bool)
|
||||
for _, dir = range dirs {
|
||||
w.tracked[dir] = false
|
||||
}
|
||||
|
||||
w.watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
for _, dir := range dirs {
|
||||
dirErrors[dir] = fmt.Errorf("failed to create watcher: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.update(dirErrors)
|
||||
}
|
||||
|
||||
// Start watching Spec directories for relevant changes.
|
||||
func (w *watch) start(m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
|
||||
go w.watch(w.watcher, m, refresh, dirErrors)
|
||||
}
|
||||
|
||||
// Stop watching directories.
|
||||
func (w *watch) stop() {
|
||||
if w.watcher == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.watcher.Close()
|
||||
w.tracked = nil
|
||||
}
|
||||
|
||||
// Watch Spec directory changes, triggering a refresh if necessary.
|
||||
func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
|
||||
watch := fsw
|
||||
if watch == nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watch.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.Op & (fsnotify.Rename | fsnotify.Remove | fsnotify.Write)) == 0 {
|
||||
continue
|
||||
}
|
||||
if event.Op == fsnotify.Write {
|
||||
if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
if event.Op == fsnotify.Remove && w.tracked[event.Name] {
|
||||
w.update(dirErrors, event.Name)
|
||||
} else {
|
||||
w.update(dirErrors)
|
||||
}
|
||||
refresh()
|
||||
m.Unlock()
|
||||
|
||||
case _, ok := <-watch.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update watch with pending/missing or removed directories.
|
||||
func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
|
||||
var (
|
||||
dir string
|
||||
ok bool
|
||||
err error
|
||||
update bool
|
||||
)
|
||||
|
||||
for dir, ok = range w.tracked {
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
err = w.watcher.Add(dir)
|
||||
if err == nil {
|
||||
w.tracked[dir] = true
|
||||
delete(dirErrors, dir)
|
||||
update = true
|
||||
} else {
|
||||
w.tracked[dir] = false
|
||||
dirErrors[dir] = fmt.Errorf("failed to monitor for changes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir = range removed {
|
||||
w.tracked[dir] = false
|
||||
dirErrors[dir] = errors.New("directory removed")
|
||||
update = true
|
||||
}
|
||||
|
||||
return update
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import "syscall"
|
||||
|
||||
func osSync() {
|
||||
syscall.Sync()
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
func osSync() {}
|
||||
@@ -1,332 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
ocigen "github.com/opencontainers/runtime-tools/generate"
|
||||
)
|
||||
|
||||
const (
|
||||
// PrestartHook is the name of the OCI "prestart" hook.
|
||||
PrestartHook = "prestart"
|
||||
// CreateRuntimeHook is the name of the OCI "createRuntime" hook.
|
||||
CreateRuntimeHook = "createRuntime"
|
||||
// CreateContainerHook is the name of the OCI "createContainer" hook.
|
||||
CreateContainerHook = "createContainer"
|
||||
// StartContainerHook is the name of the OCI "startContainer" hook.
|
||||
StartContainerHook = "startContainer"
|
||||
// PoststartHook is the name of the OCI "poststart" hook.
|
||||
PoststartHook = "poststart"
|
||||
// PoststopHook is the name of the OCI "poststop" hook.
|
||||
PoststopHook = "poststop"
|
||||
)
|
||||
|
||||
var (
|
||||
// Names of recognized hooks.
|
||||
validHookNames = map[string]struct{}{
|
||||
PrestartHook: {},
|
||||
CreateRuntimeHook: {},
|
||||
CreateContainerHook: {},
|
||||
StartContainerHook: {},
|
||||
PoststartHook: {},
|
||||
PoststopHook: {},
|
||||
}
|
||||
)
|
||||
|
||||
// ContainerEdits represent updates to be applied to an OCI Spec.
|
||||
// These updates can be specific to a CDI device, or they can be
|
||||
// specific to a CDI Spec. In the former case these edits should
|
||||
// be applied to all OCI Specs where the corresponding CDI device
|
||||
// is injected. In the latter case, these edits should be applied
|
||||
// to all OCI Specs where at least one devices from the CDI Spec
|
||||
// is injected.
|
||||
type ContainerEdits struct {
|
||||
*specs.ContainerEdits
|
||||
}
|
||||
|
||||
// Apply edits to the given OCI Spec. Updates the OCI Spec in place.
|
||||
// Returns an error if the update fails.
|
||||
func (e *ContainerEdits) Apply(spec *oci.Spec) error {
|
||||
if spec == nil {
|
||||
return errors.New("can't edit nil OCI Spec")
|
||||
}
|
||||
if e == nil || e.ContainerEdits == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
specgen := ocigen.NewFromSpec(spec)
|
||||
if len(e.Env) > 0 {
|
||||
specgen.AddMultipleProcessEnv(e.Env)
|
||||
}
|
||||
|
||||
for _, d := range e.DeviceNodes {
|
||||
dn := DeviceNode{d}
|
||||
|
||||
err := dn.fillMissingInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dev := d.ToOCI()
|
||||
if dev.UID == nil && spec.Process != nil {
|
||||
if uid := spec.Process.User.UID; uid > 0 {
|
||||
dev.UID = &uid
|
||||
}
|
||||
}
|
||||
if dev.GID == nil && spec.Process != nil {
|
||||
if gid := spec.Process.User.GID; gid > 0 {
|
||||
dev.GID = &gid
|
||||
}
|
||||
}
|
||||
|
||||
specgen.RemoveDevice(dev.Path)
|
||||
specgen.AddDevice(dev)
|
||||
|
||||
if dev.Type == "b" || dev.Type == "c" {
|
||||
access := d.Permissions
|
||||
if access == "" {
|
||||
access = "rwm"
|
||||
}
|
||||
specgen.AddLinuxResourcesDevice(true, dev.Type, &dev.Major, &dev.Minor, access)
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.Mounts) > 0 {
|
||||
for _, m := range e.Mounts {
|
||||
specgen.RemoveMount(m.ContainerPath)
|
||||
specgen.AddMount(m.ToOCI())
|
||||
}
|
||||
sortMounts(&specgen)
|
||||
}
|
||||
|
||||
for _, h := range e.Hooks {
|
||||
switch h.HookName {
|
||||
case PrestartHook:
|
||||
specgen.AddPreStartHook(h.ToOCI())
|
||||
case PoststartHook:
|
||||
specgen.AddPostStartHook(h.ToOCI())
|
||||
case PoststopHook:
|
||||
specgen.AddPostStopHook(h.ToOCI())
|
||||
// TODO: Maybe runtime-tools/generate should be updated with these...
|
||||
case CreateRuntimeHook:
|
||||
ensureOCIHooks(spec)
|
||||
spec.Hooks.CreateRuntime = append(spec.Hooks.CreateRuntime, h.ToOCI())
|
||||
case CreateContainerHook:
|
||||
ensureOCIHooks(spec)
|
||||
spec.Hooks.CreateContainer = append(spec.Hooks.CreateContainer, h.ToOCI())
|
||||
case StartContainerHook:
|
||||
ensureOCIHooks(spec)
|
||||
spec.Hooks.StartContainer = append(spec.Hooks.StartContainer, h.ToOCI())
|
||||
default:
|
||||
return fmt.Errorf("unknown hook name %q", h.HookName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate container edits.
|
||||
func (e *ContainerEdits) Validate() error {
|
||||
if e == nil || e.ContainerEdits == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ValidateEnv(e.Env); err != nil {
|
||||
return fmt.Errorf("invalid container edits: %w", err)
|
||||
}
|
||||
for _, d := range e.DeviceNodes {
|
||||
if err := (&DeviceNode{d}).Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, h := range e.Hooks {
|
||||
if err := (&Hook{h}).Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, m := range e.Mounts {
|
||||
if err := (&Mount{m}).Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Append other edits into this one. If called with a nil receiver,
|
||||
// allocates and returns newly allocated edits.
|
||||
func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits {
|
||||
if o == nil || o.ContainerEdits == nil {
|
||||
return e
|
||||
}
|
||||
if e == nil {
|
||||
e = &ContainerEdits{}
|
||||
}
|
||||
if e.ContainerEdits == nil {
|
||||
e.ContainerEdits = &specs.ContainerEdits{}
|
||||
}
|
||||
|
||||
e.Env = append(e.Env, o.Env...)
|
||||
e.DeviceNodes = append(e.DeviceNodes, o.DeviceNodes...)
|
||||
e.Hooks = append(e.Hooks, o.Hooks...)
|
||||
e.Mounts = append(e.Mounts, o.Mounts...)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// isEmpty returns true if these edits are empty. This is valid in a
|
||||
// global Spec context but invalid in a Device context.
|
||||
func (e *ContainerEdits) isEmpty() bool {
|
||||
if e == nil {
|
||||
return false
|
||||
}
|
||||
return len(e.Env)+len(e.DeviceNodes)+len(e.Hooks)+len(e.Mounts) == 0
|
||||
}
|
||||
|
||||
// ValidateEnv validates the given environment variables.
|
||||
func ValidateEnv(env []string) error {
|
||||
for _, v := range env {
|
||||
if strings.IndexByte(v, byte('=')) <= 0 {
|
||||
return fmt.Errorf("invalid environment variable %q", v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes.
|
||||
type DeviceNode struct {
|
||||
*specs.DeviceNode
|
||||
}
|
||||
|
||||
// Validate a CDI Spec DeviceNode.
|
||||
func (d *DeviceNode) Validate() error {
|
||||
validTypes := map[string]struct{}{
|
||||
"": {},
|
||||
"b": {},
|
||||
"c": {},
|
||||
"u": {},
|
||||
"p": {},
|
||||
}
|
||||
|
||||
if d.Path == "" {
|
||||
return errors.New("invalid (empty) device path")
|
||||
}
|
||||
if _, ok := validTypes[d.Type]; !ok {
|
||||
return fmt.Errorf("device %q: invalid type %q", d.Path, d.Type)
|
||||
}
|
||||
for _, bit := range d.Permissions {
|
||||
if bit != 'r' && bit != 'w' && bit != 'm' {
|
||||
return fmt.Errorf("device %q: invalid permissions %q",
|
||||
d.Path, d.Permissions)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hook is a CDI Spec Hook wrapper, used for validating hooks.
|
||||
type Hook struct {
|
||||
*specs.Hook
|
||||
}
|
||||
|
||||
// Validate a hook.
|
||||
func (h *Hook) Validate() error {
|
||||
if _, ok := validHookNames[h.HookName]; !ok {
|
||||
return fmt.Errorf("invalid hook name %q", h.HookName)
|
||||
}
|
||||
if h.Path == "" {
|
||||
return fmt.Errorf("invalid hook %q with empty path", h.HookName)
|
||||
}
|
||||
if err := ValidateEnv(h.Env); err != nil {
|
||||
return fmt.Errorf("invalid hook %q: %w", h.HookName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mount is a CDI Mount wrapper, used for validating mounts.
|
||||
type Mount struct {
|
||||
*specs.Mount
|
||||
}
|
||||
|
||||
// Validate a mount.
|
||||
func (m *Mount) Validate() error {
|
||||
if m.HostPath == "" {
|
||||
return errors.New("invalid mount, empty host path")
|
||||
}
|
||||
if m.ContainerPath == "" {
|
||||
return errors.New("invalid mount, empty container path")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure OCI Spec hooks are not nil so we can add hooks.
|
||||
func ensureOCIHooks(spec *oci.Spec) {
|
||||
if spec.Hooks == nil {
|
||||
spec.Hooks = &oci.Hooks{}
|
||||
}
|
||||
}
|
||||
|
||||
// sortMounts sorts the mounts in the given OCI Spec.
|
||||
func sortMounts(specgen *ocigen.Generator) {
|
||||
mounts := specgen.Mounts()
|
||||
specgen.ClearMounts()
|
||||
sort.Sort(orderedMounts(mounts))
|
||||
specgen.Config.Mounts = mounts
|
||||
}
|
||||
|
||||
// orderedMounts defines how to sort an OCI Spec Mount slice.
|
||||
// This is the almost the same implementation sa used by CRI-O and Docker,
|
||||
// with a minor tweak for stable sorting order (easier to test):
|
||||
//
|
||||
// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26
|
||||
type orderedMounts []oci.Mount
|
||||
|
||||
// Len returns the number of mounts. Used in sorting.
|
||||
func (m orderedMounts) Len() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
// Less returns true if the number of parts (a/b/c would be 3 parts) in the
|
||||
// mount indexed by parameter 1 is less than that of the mount indexed by
|
||||
// parameter 2. Used in sorting.
|
||||
func (m orderedMounts) Less(i, j int) bool {
|
||||
ip, jp := m.parts(i), m.parts(j)
|
||||
if ip < jp {
|
||||
return true
|
||||
}
|
||||
if jp < ip {
|
||||
return false
|
||||
}
|
||||
return m[i].Destination < m[j].Destination
|
||||
}
|
||||
|
||||
// Swap swaps two items in an array of mounts. Used in sorting
|
||||
func (m orderedMounts) Swap(i, j int) {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
}
|
||||
|
||||
// parts returns the number of parts in the destination of a mount. Used in sorting.
|
||||
func (m orderedMounts) parts(i int) int {
|
||||
return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
runc "github.com/opencontainers/runc/libcontainer/devices"
|
||||
)
|
||||
|
||||
// fillMissingInfo fills in missing mandatory attributes from the host device.
|
||||
func (d *DeviceNode) fillMissingInfo() error {
|
||||
if d.HostPath == "" {
|
||||
d.HostPath = d.Path
|
||||
}
|
||||
|
||||
if d.Type != "" && (d.Major != 0 || d.Type == "p") {
|
||||
return nil
|
||||
}
|
||||
|
||||
hostDev, err := runc.DeviceFromPath(d.HostPath, "rwm")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat CDI host device %q: %w", d.HostPath, err)
|
||||
}
|
||||
|
||||
if d.Type == "" {
|
||||
d.Type = string(hostDev.Type)
|
||||
} else {
|
||||
if d.Type != string(hostDev.Type) {
|
||||
return fmt.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)",
|
||||
d.Path, d.HostPath, d.Type, string(hostDev.Type))
|
||||
}
|
||||
}
|
||||
if d.Major == 0 && d.Type != "p" {
|
||||
d.Major = hostDev.Major
|
||||
d.Minor = hostDev.Minor
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import "fmt"
|
||||
|
||||
// fillMissingInfo fills in missing mandatory attributes from the host device.
|
||||
func (d *DeviceNode) fillMissingInfo() error {
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 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"
|
||||
)
|
||||
|
||||
// Device represents a CDI device of a Spec.
|
||||
type Device struct {
|
||||
*cdi.Device
|
||||
spec *Spec
|
||||
}
|
||||
|
||||
// Create a new Device, associate it with the given Spec.
|
||||
func newDevice(spec *Spec, d cdi.Device) (*Device, error) {
|
||||
dev := &Device{
|
||||
Device: &d,
|
||||
spec: spec,
|
||||
}
|
||||
|
||||
if err := dev.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dev, nil
|
||||
}
|
||||
|
||||
// GetSpec returns the Spec this device is defined in.
|
||||
func (d *Device) GetSpec() *Spec {
|
||||
return d.spec
|
||||
}
|
||||
|
||||
// GetQualifiedName returns the qualified name for this device.
|
||||
func (d *Device) GetQualifiedName() string {
|
||||
return parser.QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), d.Name)
|
||||
}
|
||||
|
||||
// ApplyEdits applies the device-speific container edits to an OCI Spec.
|
||||
func (d *Device) ApplyEdits(ociSpec *oci.Spec) error {
|
||||
return d.edits().Apply(ociSpec)
|
||||
}
|
||||
|
||||
// edits returns the applicable container edits for this spec.
|
||||
func (d *Device) edits() *ContainerEdits {
|
||||
return &ContainerEdits{&d.ContainerEdits}
|
||||
}
|
||||
|
||||
// Validate the device.
|
||||
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")
|
||||
}
|
||||
if err := edits.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid device %q: %w", d.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
272
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
generated
vendored
272
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
generated
vendored
@@ -1,272 +0,0 @@
|
||||
// Package cdi has the primary purpose of providing an API for
|
||||
// interacting with CDI and consuming CDI devices.
|
||||
//
|
||||
// For more information about Container Device Interface, please refer to
|
||||
// https://github.com/container-orchestrated-devices/container-device-interface
|
||||
//
|
||||
// Container Device Interface
|
||||
//
|
||||
// Container Device Interface, or CDI for short, provides comprehensive
|
||||
// third party device support for container runtimes. CDI uses vendor
|
||||
// provided specification files, CDI Specs for short, to describe how a
|
||||
// container's runtime environment should be modified when one or more
|
||||
// of the vendor-specific devices is injected into the container. Beyond
|
||||
// describing the low level platform-specific details of how to gain
|
||||
// basic access to a device, CDI Specs allow more fine-grained device
|
||||
// initialization, and the automatic injection of any necessary vendor-
|
||||
// or device-specific software that might be required for a container
|
||||
// to use a device or take full advantage of it.
|
||||
//
|
||||
// In the CDI device model containers request access to a device using
|
||||
// fully qualified device names, qualified names for short, consisting of
|
||||
// a vendor identifier, a device class and a device name or identifier.
|
||||
// These pieces of information together uniquely identify a device among
|
||||
// all device vendors, classes and device instances.
|
||||
//
|
||||
// This package implements an API for easy consumption of CDI. The API
|
||||
// implements discovery, loading and caching of CDI Specs and injection
|
||||
// of CDI devices into containers. This is the most common functionality
|
||||
// the vast majority of CDI consumers need. The API should be usable both
|
||||
// by OCI runtime clients and runtime implementations.
|
||||
//
|
||||
// CDI Registry
|
||||
//
|
||||
// The primary interface to interact with CDI devices is the Registry. It
|
||||
// is essentially a cache of all Specs and devices discovered in standard
|
||||
// CDI directories on the host. The registry has two main functionality,
|
||||
// injecting devices into an OCI Spec and refreshing the cache of CDI
|
||||
// Specs and devices.
|
||||
//
|
||||
// Device Injection
|
||||
//
|
||||
// Using the Registry one can inject CDI devices into a container with code
|
||||
// similar to the following snippet:
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "strings"
|
||||
//
|
||||
// log "github.com/sirupsen/logrus"
|
||||
//
|
||||
// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
||||
// oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
// )
|
||||
//
|
||||
// func injectCDIDevices(spec *oci.Spec, devices []string) error {
|
||||
// log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
|
||||
//
|
||||
// unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("CDI device injection failed: %w", err)
|
||||
// }
|
||||
//
|
||||
// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec))
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// Cache Refresh
|
||||
//
|
||||
// By default the CDI Spec cache monitors the configured Spec directories
|
||||
// and automatically refreshes itself when necessary. This behavior can be
|
||||
// disabled using the WithAutoRefresh(false) option.
|
||||
//
|
||||
// Failure to set up monitoring for a Spec directory causes the directory to
|
||||
// get ignored and an error to be recorded among the Spec directory errors.
|
||||
// These errors can be queried using the GetSpecDirErrors() function. If the
|
||||
// error condition is transient, for instance a missing directory which later
|
||||
// gets created, the corresponding error will be removed once the condition
|
||||
// is over.
|
||||
//
|
||||
// With auto-refresh enabled injecting any CDI devices can be done without
|
||||
// an explicit call to Refresh(), using a code snippet similar to the
|
||||
// following:
|
||||
//
|
||||
// In a runtime implementation one typically wants to make sure the
|
||||
// CDI Spec cache is up to date before performing device injection.
|
||||
// A code snippet similar to the following accmplishes that:
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "strings"
|
||||
//
|
||||
// log "github.com/sirupsen/logrus"
|
||||
//
|
||||
// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
||||
// oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
// )
|
||||
//
|
||||
// func injectCDIDevices(spec *oci.Spec, devices []string) error {
|
||||
// registry := cdi.GetRegistry()
|
||||
//
|
||||
// if err := registry.Refresh(); err != nil {
|
||||
// // Note:
|
||||
// // It is up to the implementation to decide whether
|
||||
// // to abort injection on errors. A failed Refresh()
|
||||
// // does not necessarily render the registry unusable.
|
||||
// // For instance, a parse error in a Spec file for
|
||||
// // vendor A does not have any effect on devices of
|
||||
// // vendor B...
|
||||
// log.Warnf("pre-injection Refresh() failed: %v", err)
|
||||
// }
|
||||
//
|
||||
// log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
|
||||
//
|
||||
// unresolved, err := registry.InjectDevices(spec, devices)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("CDI device injection failed: %w", err)
|
||||
// }
|
||||
//
|
||||
// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec))
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// Generated Spec Files, Multiple Directories, Device Precedence
|
||||
//
|
||||
// It is often necessary to generate Spec files dynamically. On some
|
||||
// systems the available or usable set of CDI devices might change
|
||||
// dynamically which then needs to be reflected in CDI Specs. For
|
||||
// some device classes it makes sense to enumerate the available
|
||||
// devices at every boot and generate Spec file entries for each
|
||||
// device found. Some CDI devices might need special client- or
|
||||
// request-specific configuration which can only be fulfilled by
|
||||
// dynamically generated client-specific entries in transient Spec
|
||||
// files.
|
||||
//
|
||||
// CDI can collect Spec files from multiple directories. Spec files are
|
||||
// automatically assigned priorities according to which directory they
|
||||
// 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 choosing the definition from the
|
||||
// Spec file with the highest priority.
|
||||
//
|
||||
// The default CDI directory configuration is chosen to encourage
|
||||
// separating dynamically generated CDI Spec files from static ones.
|
||||
// The default directories are '/etc/cdi' and '/var/run/cdi'. By putting
|
||||
// dynamically generated Spec files under '/var/run/cdi', those take
|
||||
// precedence over static ones in '/etc/cdi'. With this scheme, static
|
||||
// Spec files, typically installed by distro-specific packages, go into
|
||||
// '/etc/cdi' while all the dynamically generated Spec files, transient
|
||||
// or other, go into '/var/run/cdi'.
|
||||
//
|
||||
// Spec File Generation
|
||||
//
|
||||
// CDI offers two functions for writing and removing dynamically generated
|
||||
// Specs from CDI Spec directories. These functions, WriteSpec() and
|
||||
// RemoveSpec() implicitly follow the principle of separating dynamic Specs
|
||||
// from the rest and therefore always write to and remove Specs from the
|
||||
// last configured directory.
|
||||
//
|
||||
// Corresponding functions are also provided for generating names for Spec
|
||||
// files. These functions follow a simple naming convention to ensure that
|
||||
// multiple entities generating Spec files simultaneously on the same host
|
||||
// do not end up using conflicting Spec file names. GenerateSpecName(),
|
||||
// GenerateNameForSpec(), GenerateTransientSpecName(), and
|
||||
// GenerateTransientNameForSpec() all generate names which can be passed
|
||||
// as such to WriteSpec() and subsequently to RemoveSpec().
|
||||
//
|
||||
// Generating a Spec file for a vendor/device class can be done with a
|
||||
// code snippet similar to the following:
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// ...
|
||||
// "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
||||
// )
|
||||
//
|
||||
// func generateDeviceSpecs() error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// spec := &specs.Spec{
|
||||
// Version: specs.CurrentVersion,
|
||||
// Kind: vendor+"/"+class,
|
||||
// }
|
||||
//
|
||||
// for _, dev := range enumerateDevices() {
|
||||
// spec.Devices = append(spec.Devices, specs.Device{
|
||||
// Name: dev.Name,
|
||||
// ContainerEdits: getContainerEditsForDevice(dev),
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// specName, err := cdi.GenerateNameForSpec(spec)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to generate Spec name: %w", err)
|
||||
// }
|
||||
//
|
||||
// return registry.SpecDB().WriteSpec(spec, specName)
|
||||
// }
|
||||
//
|
||||
// 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
|
||||
// and removed once that container is removed.
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// ...
|
||||
// "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
||||
// )
|
||||
//
|
||||
// func generateTransientSpec(ctr Container) error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// devices := getContainerDevs(ctr, vendor, class)
|
||||
// spec := &specs.Spec{
|
||||
// Version: specs.CurrentVersion,
|
||||
// Kind: vendor+"/"+class,
|
||||
// }
|
||||
//
|
||||
// for _, dev := range devices {
|
||||
// spec.Devices = append(spec.Devices, specs.Device{
|
||||
// // the generated name needs to be unique within the
|
||||
// // vendor/class domain on the host/node.
|
||||
// Name: generateUniqueDevName(dev, ctr),
|
||||
// ContainerEdits: getEditsForContainer(dev),
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// // transientID is expected to guarantee that the Spec file name
|
||||
// // generated using <vendor, class, transientID> is unique within
|
||||
// // the host/node. If more than one device is allocated with the
|
||||
// // same vendor/class domain, either all generated Spec entries
|
||||
// // should go to a single Spec file (like in this sample snippet),
|
||||
// // or transientID should be unique for each generated Spec file.
|
||||
// transientID := getSomeSufficientlyUniqueIDForContainer(ctr)
|
||||
// specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to generate Spec name: %w", err)
|
||||
// }
|
||||
//
|
||||
// return registry.SpecDB().WriteSpec(spec, specName)
|
||||
// }
|
||||
//
|
||||
// func removeTransientSpec(ctr Container) error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// transientID := getSomeSufficientlyUniqueIDForContainer(ctr)
|
||||
// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID)
|
||||
//
|
||||
// return registry.SpecDB().RemoveSpec(specName)
|
||||
// }
|
||||
//
|
||||
// CDI Spec Validation
|
||||
//
|
||||
// This package performs both syntactic and semantic validation of CDI
|
||||
// Spec file data when a Spec file is loaded via the registry or using
|
||||
// the ReadSpec API function. As part of the semantic verification, the
|
||||
// Spec file is verified against the CDI Spec JSON validation schema.
|
||||
//
|
||||
// If a valid externally provided JSON validation schema is found in
|
||||
// the filesystem at /etc/cdi/schema/schema.json it is loaded and used
|
||||
// as the default validation schema. If such a file is not found or
|
||||
// fails to load, an embedded no-op schema is used.
|
||||
//
|
||||
// The used validation schema can also be changed programmatically using
|
||||
// the SetSchema API convenience function. This function also accepts
|
||||
// the special "builtin" (BuiltinSchemaName) and "none" (NoneSchemaName)
|
||||
// schema names which switch the used schema to the in-repo validation
|
||||
// schema embedded into the binary or the now default no-op schema
|
||||
// correspondingly. Other names are interpreted as the path to the actual
|
||||
// validation schema to load and use.
|
||||
package cdi
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"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 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 parser.QualifiedName(vendor, class, name)
|
||||
}
|
||||
|
||||
// IsQualifiedName tests if a device name is qualified.
|
||||
//
|
||||
// Deprecated: use parser.IsQualifiedName instead
|
||||
func IsQualifiedName(device string) bool {
|
||||
return parser.IsQualifiedName(device)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Deprecated: use parser.ParseQualifiedName instead
|
||||
func ParseQualifiedName(device string) (string, string, string, error) {
|
||||
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) {
|
||||
return parser.ParseDevice(device)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Deprecated: use parser.ParseQualifier instead
|
||||
func ParseQualifier(kind string) (string, string) {
|
||||
return parser.ParseQualifier(kind)
|
||||
}
|
||||
|
||||
// ValidateVendorName checks the validity of a vendor name.
|
||||
// A vendor name may contain the following ASCII characters:
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, and dot ('_', '-', and '.')
|
||||
//
|
||||
// Deprecated: use parser.ValidateVendorName instead
|
||||
func ValidateVendorName(vendor string) error {
|
||||
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, dash, and dot ('_', '-', and '.')
|
||||
//
|
||||
// Deprecated: use parser.ValidateClassName instead
|
||||
func ValidateClassName(class string) error {
|
||||
return parser.ValidateClassName(class)
|
||||
}
|
||||
|
||||
// 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 ('_', '-', '.', ':')
|
||||
//
|
||||
// Deprecated: use parser.ValidateDeviceName instead
|
||||
func ValidateDeviceName(name string) error {
|
||||
return parser.ValidateDeviceName(name)
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||
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
|
||||
DeviceDB() RegistryDeviceDB
|
||||
SpecDB() RegistrySpecDB
|
||||
}
|
||||
|
||||
// RegistryRefresher is the registry interface for refreshing the
|
||||
// cache of CDI Specs and devices.
|
||||
//
|
||||
// Configure reconfigures the registry with the given options.
|
||||
//
|
||||
// Refresh rescans all CDI Spec directories and updates the
|
||||
// state of the cache to reflect any changes. It returns any
|
||||
// errors encountered during the refresh.
|
||||
//
|
||||
// GetErrors returns all errors encountered for any of the scanned
|
||||
// Spec files during the last cache refresh.
|
||||
//
|
||||
// GetSpecDirectories returns the set up CDI Spec directories
|
||||
// currently in use. The directories are returned in the scan
|
||||
// order of Refresh().
|
||||
//
|
||||
// GetSpecDirErrors returns any errors related to the configured
|
||||
// Spec directories.
|
||||
type RegistryRefresher interface {
|
||||
Configure(...Option) error
|
||||
Refresh() error
|
||||
GetErrors() map[string][]error
|
||||
GetSpecDirectories() []string
|
||||
GetSpecDirErrors() map[string]error
|
||||
}
|
||||
|
||||
// RegistryResolver is the registry interface for injecting CDI
|
||||
// devices into an OCI Spec.
|
||||
//
|
||||
// InjectDevices takes an OCI Spec and injects into it a set of
|
||||
// CDI devices given by qualified name. It returns the names of
|
||||
// any unresolved devices and an error if injection fails.
|
||||
type RegistryResolver interface {
|
||||
InjectDevices(spec *oci.Spec, device ...string) (unresolved []string, err error)
|
||||
}
|
||||
|
||||
// RegistryDeviceDB is the registry interface for querying devices.
|
||||
//
|
||||
// GetDevice returns the CDI device for the given qualified name. If
|
||||
// the device is not GetDevice returns nil.
|
||||
//
|
||||
// ListDevices returns a slice with the names of qualified device
|
||||
// known. The returned slice is sorted.
|
||||
type RegistryDeviceDB interface {
|
||||
GetDevice(device string) *Device
|
||||
ListDevices() []string
|
||||
}
|
||||
|
||||
// RegistrySpecDB is the registry interface for querying CDI Specs.
|
||||
//
|
||||
// ListVendors returns a slice with all vendors known. The returned
|
||||
// slice is sorted.
|
||||
//
|
||||
// ListClasses returns a slice with all classes known. The returned
|
||||
// slice is sorted.
|
||||
//
|
||||
// GetVendorSpecs returns a slice of all Specs for the vendor.
|
||||
//
|
||||
// GetSpecErrors returns any errors for the Spec encountered during
|
||||
// the last cache refresh.
|
||||
//
|
||||
// WriteSpec writes the Spec with the given content and name to the
|
||||
// last Spec directory.
|
||||
type RegistrySpecDB interface {
|
||||
ListVendors() []string
|
||||
ListClasses() []string
|
||||
GetVendorSpecs(vendor string) []*Spec
|
||||
GetSpecErrors(*Spec) []error
|
||||
WriteSpec(raw *cdi.Spec, name string) error
|
||||
RemoveSpec(name string) error
|
||||
}
|
||||
|
||||
type registry struct {
|
||||
*Cache
|
||||
}
|
||||
|
||||
var _ Registry = ®istry{}
|
||||
|
||||
var (
|
||||
reg *registry
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
// GetRegistry returns the CDI registry. If any options are given, those
|
||||
// are applied to the registry.
|
||||
func GetRegistry(options ...Option) Registry {
|
||||
var new bool
|
||||
initOnce.Do(func() {
|
||||
reg, _ = getRegistry(options...)
|
||||
new = true
|
||||
})
|
||||
if !new && len(options) > 0 {
|
||||
reg.Configure(options...)
|
||||
reg.Refresh()
|
||||
}
|
||||
return reg
|
||||
}
|
||||
|
||||
// DeviceDB returns the registry interface for querying devices.
|
||||
func (r *registry) DeviceDB() RegistryDeviceDB {
|
||||
return r
|
||||
}
|
||||
|
||||
// SpecDB returns the registry interface for querying Specs.
|
||||
func (r *registry) SpecDB() RegistrySpecDB {
|
||||
return r
|
||||
}
|
||||
|
||||
func getRegistry(options ...Option) (*registry, error) {
|
||||
c, err := NewCache(options...)
|
||||
return ®istry{c}, err
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultStaticDir is the default directory for static CDI Specs.
|
||||
DefaultStaticDir = "/etc/cdi"
|
||||
// DefaultDynamicDir is the default directory for generated CDI Specs
|
||||
DefaultDynamicDir = "/var/run/cdi"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultSpecDirs is the default Spec directory configuration.
|
||||
// While altering this variable changes the package defaults,
|
||||
// the preferred way of overriding the default directories is
|
||||
// to use a WithSpecDirs options. Otherwise the change is only
|
||||
// effective if it takes place before creating the Registry or
|
||||
// other Cache instances.
|
||||
DefaultSpecDirs = []string{DefaultStaticDir, DefaultDynamicDir}
|
||||
// ErrStopScan can be returned from a ScanSpecFunc to stop the scan.
|
||||
ErrStopScan = errors.New("stop Spec scan")
|
||||
)
|
||||
|
||||
// WithSpecDirs returns an option to override the CDI Spec directories.
|
||||
func WithSpecDirs(dirs ...string) Option {
|
||||
return func(c *Cache) error {
|
||||
specDirs := make([]string, len(dirs))
|
||||
for i, dir := range dirs {
|
||||
specDirs[i] = filepath.Clean(dir)
|
||||
}
|
||||
c.specDirs = specDirs
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// scanSpecFunc is a function for processing CDI Spec files.
|
||||
type scanSpecFunc func(string, int, *Spec, error) error
|
||||
|
||||
// ScanSpecDirs scans the given directories looking for CDI Spec files,
|
||||
// which are all files with a '.json' or '.yaml' suffix. For every Spec
|
||||
// file discovered, ScanSpecDirs loads a Spec from the file then calls
|
||||
// the scan function passing it the path to the file, the priority (the
|
||||
// index of the directory in the slice of directories given), the Spec
|
||||
// itself, and any error encountered while loading the Spec.
|
||||
//
|
||||
// Scanning stops once all files have been processed or when the scan
|
||||
// function returns an error. The result of ScanSpecDirs is the error
|
||||
// returned by the scan function, if any. The special error ErrStopScan
|
||||
// can be used to terminate the scan gracefully without ScanSpecDirs
|
||||
// returning an error. ScanSpecDirs silently skips any subdirectories.
|
||||
func scanSpecDirs(dirs []string, scanFn scanSpecFunc) error {
|
||||
var (
|
||||
spec *Spec
|
||||
err error
|
||||
)
|
||||
|
||||
for priority, dir := range dirs {
|
||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
// for initial stat failure Walk calls us with nil info
|
||||
if info == nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// first call from Walk is for dir itself, others we skip
|
||||
if info.IsDir() {
|
||||
if path == dir {
|
||||
return nil
|
||||
}
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
// ignore obviously non-Spec files
|
||||
if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return scanFn(path, priority, nil, err)
|
||||
}
|
||||
|
||||
spec, err = ReadSpec(path, priority)
|
||||
return scanFn(path, priority, spec, err)
|
||||
})
|
||||
|
||||
if err != nil && err != ErrStopScan {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
352
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
generated
vendored
352
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
generated
vendored
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 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 cdi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultSpecExt is the file extension for the default encoding.
|
||||
defaultSpecExt = ".yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
// Externally set CDI Spec validation function.
|
||||
specValidator func(*cdi.Spec) error
|
||||
validatorLock sync.RWMutex
|
||||
)
|
||||
|
||||
// Spec represents a single CDI Spec. It is usually loaded from a
|
||||
// file and stored in a cache. The Spec has an associated priority.
|
||||
// This priority is inherited from the associated priority of the
|
||||
// CDI Spec directory that contains the CDI Spec file and is used
|
||||
// to resolve conflicts if multiple CDI Spec files contain entries
|
||||
// for the same fully qualified device.
|
||||
type Spec struct {
|
||||
*cdi.Spec
|
||||
vendor string
|
||||
class string
|
||||
path string
|
||||
priority int
|
||||
devices map[string]*Device
|
||||
}
|
||||
|
||||
// ReadSpec reads the given CDI Spec file. The resulting Spec is
|
||||
// assigned the given priority. If reading or parsing the Spec
|
||||
// data fails ReadSpec returns a nil Spec and an error.
|
||||
func ReadSpec(path string, priority int) (*Spec, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return nil, err
|
||||
case err != nil:
|
||||
return nil, fmt.Errorf("failed to read CDI Spec %q: %w", path, err)
|
||||
}
|
||||
|
||||
raw, err := ParseSpec(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse CDI Spec %q: %w", path, err)
|
||||
}
|
||||
if raw == nil {
|
||||
return nil, fmt.Errorf("failed to parse CDI Spec %q, no Spec data", path)
|
||||
}
|
||||
|
||||
spec, err := newSpec(raw, path, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// newSpec creates a new Spec from the given CDI Spec data. The
|
||||
// Spec is marked as loaded from the given path with the given
|
||||
// priority. If Spec data validation fails newSpec returns a nil
|
||||
// Spec and an error.
|
||||
func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
|
||||
err := validateSpec(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec := &Spec{
|
||||
Spec: raw,
|
||||
path: filepath.Clean(path),
|
||||
priority: priority,
|
||||
}
|
||||
|
||||
if ext := filepath.Ext(spec.path); ext != ".yaml" && ext != ".json" {
|
||||
spec.path += defaultSpecExt
|
||||
}
|
||||
|
||||
spec.vendor, spec.class = ParseQualifier(spec.Kind)
|
||||
|
||||
if spec.devices, err = spec.validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid CDI Spec: %w", err)
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// Write the CDI Spec to the file associated with it during instantiation
|
||||
// by newSpec() or ReadSpec().
|
||||
func (s *Spec) write(overwrite bool) error {
|
||||
var (
|
||||
data []byte
|
||||
dir string
|
||||
tmp *os.File
|
||||
err error
|
||||
)
|
||||
|
||||
err = validateSpec(s.Spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if filepath.Ext(s.path) == ".yaml" {
|
||||
data, err = yaml.Marshal(s.Spec)
|
||||
data = append([]byte("---\n"), data...)
|
||||
} else {
|
||||
data, err = json.Marshal(s.Spec)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Spec file: %w", err)
|
||||
}
|
||||
|
||||
dir = filepath.Dir(s.path)
|
||||
err = os.MkdirAll(dir, 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Spec dir: %w", err)
|
||||
}
|
||||
|
||||
tmp, err = os.CreateTemp(dir, "spec.*.tmp")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Spec file: %w", err)
|
||||
}
|
||||
_, err = tmp.Write(data)
|
||||
tmp.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write Spec file: %w", err)
|
||||
}
|
||||
|
||||
err = renameIn(dir, filepath.Base(tmp.Name()), filepath.Base(s.path), overwrite)
|
||||
|
||||
if err != nil {
|
||||
os.Remove(tmp.Name())
|
||||
err = fmt.Errorf("failed to write Spec file: %w", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetVendor returns the vendor of this Spec.
|
||||
func (s *Spec) GetVendor() string {
|
||||
return s.vendor
|
||||
}
|
||||
|
||||
// GetClass returns the device class of this Spec.
|
||||
func (s *Spec) GetClass() string {
|
||||
return s.class
|
||||
}
|
||||
|
||||
// GetDevice returns the device for the given unqualified name.
|
||||
func (s *Spec) GetDevice(name string) *Device {
|
||||
return s.devices[name]
|
||||
}
|
||||
|
||||
// GetPath returns the filesystem path of this Spec.
|
||||
func (s *Spec) GetPath() string {
|
||||
return s.path
|
||||
}
|
||||
|
||||
// GetPriority returns the priority of this Spec.
|
||||
func (s *Spec) GetPriority() int {
|
||||
return s.priority
|
||||
}
|
||||
|
||||
// ApplyEdits applies the Spec's global-scope container edits to an OCI Spec.
|
||||
func (s *Spec) ApplyEdits(ociSpec *oci.Spec) error {
|
||||
return s.edits().Apply(ociSpec)
|
||||
}
|
||||
|
||||
// edits returns the applicable global container edits for this spec.
|
||||
func (s *Spec) edits() *ContainerEdits {
|
||||
return &ContainerEdits{&s.ContainerEdits}
|
||||
}
|
||||
|
||||
// Validate the Spec.
|
||||
func (s *Spec) validate() (map[string]*Device, error) {
|
||||
if err := validateVersion(s.Version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minVersion, err := MinimumRequiredVersion(s.Spec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine minimum required version: %v", err)
|
||||
}
|
||||
if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) {
|
||||
return nil, fmt.Errorf("the spec version must be at least v%v", minVersion)
|
||||
}
|
||||
|
||||
if err := ValidateVendorName(s.vendor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
devices := make(map[string]*Device)
|
||||
for _, d := range s.Devices {
|
||||
dev, err := newDevice(s, d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed add device %q: %w", d.Name, err)
|
||||
}
|
||||
if _, conflict := devices[d.Name]; conflict {
|
||||
return nil, fmt.Errorf("invalid spec, multiple device %q", d.Name)
|
||||
}
|
||||
devices[d.Name] = dev
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// validateVersion checks whether the specified spec version is supported.
|
||||
func validateVersion(version string) error {
|
||||
if !validSpecVersions.isValidVersion(version) {
|
||||
return fmt.Errorf("invalid version %q", version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseSpec parses CDI Spec data into a raw CDI Spec.
|
||||
func ParseSpec(data []byte) (*cdi.Spec, error) {
|
||||
var raw *cdi.Spec
|
||||
err := yaml.UnmarshalStrict(data, &raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal CDI Spec: %w", err)
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// SetSpecValidator sets a CDI Spec validator function. This function
|
||||
// is used for extra CDI Spec content validation whenever a Spec file
|
||||
// loaded (using ReadSpec() or written (using WriteSpec()).
|
||||
func SetSpecValidator(fn func(*cdi.Spec) error) {
|
||||
validatorLock.Lock()
|
||||
defer validatorLock.Unlock()
|
||||
specValidator = fn
|
||||
}
|
||||
|
||||
// validateSpec validates the Spec using the extneral validator.
|
||||
func validateSpec(raw *cdi.Spec) error {
|
||||
validatorLock.RLock()
|
||||
defer validatorLock.RUnlock()
|
||||
|
||||
if specValidator == nil {
|
||||
return nil
|
||||
}
|
||||
err := specValidator(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Spec validation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateSpecName generates a vendor+class scoped Spec file name. The
|
||||
// name can be passed to WriteSpec() to write a Spec file to the file
|
||||
// system.
|
||||
//
|
||||
// vendor and class should match the vendor and class of the CDI Spec.
|
||||
// The file name is generated without a ".json" or ".yaml" extension.
|
||||
// The caller can append the desired extension to choose a particular
|
||||
// encoding. Otherwise WriteSpec() will use its default encoding.
|
||||
//
|
||||
// This function always returns the same name for the same vendor/class
|
||||
// combination. Therefore it cannot be used as such to generate multiple
|
||||
// Spec file names for a single vendor and class.
|
||||
func GenerateSpecName(vendor, class string) string {
|
||||
return vendor + "-" + class
|
||||
}
|
||||
|
||||
// GenerateTransientSpecName generates a vendor+class scoped transient
|
||||
// Spec file name. The name can be passed to WriteSpec() to write a Spec
|
||||
// file to the file system.
|
||||
//
|
||||
// Transient Specs are those whose lifecycle is tied to that of some
|
||||
// external entity, for instance a container. vendor and class should
|
||||
// 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 transient Spec
|
||||
// is tied to has a unique ID of its own, then this is usually a
|
||||
// good choice for transientID.
|
||||
//
|
||||
// The file name is generated without a ".json" or ".yaml" extension.
|
||||
// The caller can append the desired extension to choose a particular
|
||||
// encoding. Otherwise WriteSpec() will use its default encoding.
|
||||
func GenerateTransientSpecName(vendor, class, transientID string) string {
|
||||
transientID = strings.ReplaceAll(transientID, "/", "_")
|
||||
return GenerateSpecName(vendor, class) + "_" + transientID
|
||||
}
|
||||
|
||||
// GenerateNameForSpec generates a name for the given Spec using
|
||||
// GenerateSpecName with the vendor and class taken from the Spec.
|
||||
// On success it returns the generated name and a nil error. If
|
||||
// the Spec does not contain a valid vendor or class, it returns
|
||||
// an empty name and a non-nil error.
|
||||
func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
|
||||
vendor, class := ParseQualifier(raw.Kind)
|
||||
if vendor == "" {
|
||||
return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
|
||||
}
|
||||
|
||||
return GenerateSpecName(vendor, class), nil
|
||||
}
|
||||
|
||||
// GenerateNameForTransientSpec generates a name for the given transient
|
||||
// Spec using GenerateTransientSpecName with the vendor and class taken
|
||||
// from the Spec. On success it returns the generated name and a nil error.
|
||||
// If the Spec does not contain a valid vendor or class, it returns an
|
||||
// an empty name and a non-nil error.
|
||||
func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) {
|
||||
vendor, class := ParseQualifier(raw.Kind)
|
||||
if vendor == "" {
|
||||
return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
|
||||
}
|
||||
|
||||
return GenerateTransientSpecName(vendor, class, transientID), nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright © 2022 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 cdi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Rename src to dst, both relative to the directory dir. If dst already exists
|
||||
// refuse renaming with an error unless overwrite is explicitly asked for.
|
||||
func renameIn(dir, src, dst string, overwrite bool) error {
|
||||
var flags uint
|
||||
|
||||
dirf, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rename failed: %w", err)
|
||||
}
|
||||
defer dirf.Close()
|
||||
|
||||
if !overwrite {
|
||||
flags = unix.RENAME_NOREPLACE
|
||||
}
|
||||
|
||||
dirFd := int(dirf.Fd())
|
||||
err = unix.Renameat2(dirFd, src, dirFd, dst, flags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rename failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright © 2022 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 cdi
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Rename src to dst, both relative to the directory dir. If dst already exists
|
||||
// refuse renaming with an error unless overwrite is explicitly asked for.
|
||||
func renameIn(dir, src, dst string, overwrite bool) error {
|
||||
src = filepath.Join(dir, src)
|
||||
dst = filepath.Join(dir, dst)
|
||||
|
||||
_, err := os.Stat(dst)
|
||||
if err == nil && !overwrite {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
return os.Rename(src, dst)
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
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 cdi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
// CurrentVersion is the current version of the CDI Spec.
|
||||
CurrentVersion = cdi.CurrentVersion
|
||||
|
||||
// vCurrent is the current version as a semver-comparable type
|
||||
vCurrent version = "v" + CurrentVersion
|
||||
|
||||
// These represent the released versions of the CDI specification
|
||||
v010 version = "v0.1.0"
|
||||
v020 version = "v0.2.0"
|
||||
v030 version = "v0.3.0"
|
||||
v040 version = "v0.4.0"
|
||||
v050 version = "v0.5.0"
|
||||
v060 version = "v0.6.0"
|
||||
|
||||
// vEarliest is the earliest supported version of the CDI specification
|
||||
vEarliest version = v030
|
||||
)
|
||||
|
||||
// validSpecVersions stores a map of spec versions to functions to check the required versions.
|
||||
// Adding new fields / spec versions requires that a `requiredFunc` be implemented and
|
||||
// this map be updated.
|
||||
var validSpecVersions = requiredVersionMap{
|
||||
v010: nil,
|
||||
v020: nil,
|
||||
v030: nil,
|
||||
v040: requiresV040,
|
||||
v050: requiresV050,
|
||||
v060: requiresV060,
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// version represents a semantic version string
|
||||
type version string
|
||||
|
||||
// newVersion creates a version that can be used for semantic version comparisons.
|
||||
func newVersion(v string) version {
|
||||
return version("v" + strings.TrimPrefix(v, "v"))
|
||||
}
|
||||
|
||||
// String returns the string representation of the version.
|
||||
// This trims a leading v if present.
|
||||
func (v version) String() string {
|
||||
return strings.TrimPrefix(string(v), "v")
|
||||
}
|
||||
|
||||
// IsGreaterThan checks with a version is greater than the specified version.
|
||||
func (v version) IsGreaterThan(o version) bool {
|
||||
return semver.Compare(string(v), string(o)) > 0
|
||||
}
|
||||
|
||||
// IsLatest checks whether the version is the latest supported version
|
||||
func (v version) IsLatest() bool {
|
||||
return v == vCurrent
|
||||
}
|
||||
|
||||
type requiredFunc func(*cdi.Spec) bool
|
||||
|
||||
type requiredVersionMap map[version]requiredFunc
|
||||
|
||||
// isValidVersion checks whether the specified version is valid.
|
||||
// A version is valid if it is contained in the required version map.
|
||||
func (r requiredVersionMap) isValidVersion(specVersion string) bool {
|
||||
_, ok := validSpecVersions[newVersion(specVersion)]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// requiredVersion returns the minimum version required for the given spec
|
||||
func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
|
||||
minVersion := vEarliest
|
||||
|
||||
for v, isRequired := range validSpecVersions {
|
||||
if isRequired == nil {
|
||||
continue
|
||||
}
|
||||
if isRequired(spec) && v.IsGreaterThan(minVersion) {
|
||||
minVersion = v
|
||||
}
|
||||
// If we have already detected the latest version then no later version could be detected
|
||||
if minVersion.IsLatest() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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 && !parser.IsLetter(rune(d.Name[0])) {
|
||||
return true
|
||||
}
|
||||
edits = append(edits, &d.ContainerEdits)
|
||||
}
|
||||
|
||||
edits = append(edits, &spec.ContainerEdits)
|
||||
for _, e := range edits {
|
||||
for _, dn := range e.DeviceNodes {
|
||||
// The HostPath field was added in v0.5.0
|
||||
if dn.HostPath != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// requiresV040 returns true if the spec uses v0.4.0 features
|
||||
func requiresV040(spec *cdi.Spec) bool {
|
||||
var edits []*cdi.ContainerEdits
|
||||
|
||||
for _, d := range spec.Devices {
|
||||
edits = append(edits, &d.ContainerEdits)
|
||||
}
|
||||
|
||||
edits = append(edits, &spec.ContainerEdits)
|
||||
for _, e := range edits {
|
||||
for _, m := range e.Mounts {
|
||||
// The Type field was added in v0.4.0
|
||||
if m.Type != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package specs
|
||||
|
||||
import "os"
|
||||
|
||||
// CurrentVersion is the current version of the Spec.
|
||||
const CurrentVersion = "0.6.0"
|
||||
|
||||
// Spec is the base configuration for CDI
|
||||
type Spec struct {
|
||||
Version string `json:"cdiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
// 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"`
|
||||
// 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.
|
||||
type ContainerEdits struct {
|
||||
Env []string `json:"env,omitempty"`
|
||||
DeviceNodes []*DeviceNode `json:"deviceNodes,omitempty"`
|
||||
Hooks []*Hook `json:"hooks,omitempty"`
|
||||
Mounts []*Mount `json:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
// DeviceNode represents a device node that needs to be added to the OCI spec.
|
||||
type DeviceNode struct {
|
||||
Path string `json:"path"`
|
||||
HostPath string `json:"hostPath,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Major int64 `json:"major,omitempty"`
|
||||
Minor int64 `json:"minor,omitempty"`
|
||||
FileMode *os.FileMode `json:"fileMode,omitempty"`
|
||||
Permissions string `json:"permissions,omitempty"`
|
||||
UID *uint32 `json:"uid,omitempty"`
|
||||
GID *uint32 `json:"gid,omitempty"`
|
||||
}
|
||||
|
||||
// Mount represents a mount that needs to be added to the OCI spec.
|
||||
type Mount struct {
|
||||
HostPath string `json:"hostPath"`
|
||||
ContainerPath string `json:"containerPath"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Hook represents a hook that needs to be added to the OCI spec.
|
||||
type Hook struct {
|
||||
HookName string `json:"hookName"`
|
||||
Path string `json:"path"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
Env []string `json:"env,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty"`
|
||||
}
|
||||
113
vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go
generated
vendored
113
vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go
generated
vendored
@@ -1,113 +0,0 @@
|
||||
package specs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// ApplyOCIEditsForDevice applies devices OCI edits, in other words
|
||||
// it finds the device in the CDI spec and applies the OCI patches that device
|
||||
// requires to the OCI specification.
|
||||
func ApplyOCIEditsForDevice(config *spec.Spec, cdi *Spec, dev string) error {
|
||||
for _, d := range cdi.Devices {
|
||||
if d.Name != dev {
|
||||
continue
|
||||
}
|
||||
|
||||
return ApplyEditsToOCISpec(config, &d.ContainerEdits)
|
||||
}
|
||||
|
||||
return fmt.Errorf("CDI: device %q not found for spec %q", dev, cdi.Kind)
|
||||
}
|
||||
|
||||
// ApplyOCIEdits applies the OCI edits the CDI spec declares globally
|
||||
func ApplyOCIEdits(config *spec.Spec, cdi *Spec) error {
|
||||
return ApplyEditsToOCISpec(config, &cdi.ContainerEdits)
|
||||
}
|
||||
|
||||
// ApplyEditsToOCISpec applies the specified edits to the OCI spec.
|
||||
func ApplyEditsToOCISpec(config *spec.Spec, edits *ContainerEdits) error {
|
||||
if config == nil {
|
||||
return errors.New("spec is nil")
|
||||
}
|
||||
if edits == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(edits.Env) > 0 {
|
||||
if config.Process == nil {
|
||||
config.Process = &spec.Process{}
|
||||
}
|
||||
config.Process.Env = append(config.Process.Env, edits.Env...)
|
||||
}
|
||||
|
||||
for _, d := range edits.DeviceNodes {
|
||||
if config.Linux == nil {
|
||||
config.Linux = &spec.Linux{}
|
||||
}
|
||||
config.Linux.Devices = append(config.Linux.Devices, d.ToOCI())
|
||||
}
|
||||
|
||||
for _, m := range edits.Mounts {
|
||||
config.Mounts = append(config.Mounts, m.ToOCI())
|
||||
}
|
||||
|
||||
for _, h := range edits.Hooks {
|
||||
if config.Hooks == nil {
|
||||
config.Hooks = &spec.Hooks{}
|
||||
}
|
||||
switch h.HookName {
|
||||
case "prestart":
|
||||
config.Hooks.Prestart = append(config.Hooks.Prestart, h.ToOCI())
|
||||
case "createRuntime":
|
||||
config.Hooks.CreateRuntime = append(config.Hooks.CreateRuntime, h.ToOCI())
|
||||
case "createContainer":
|
||||
config.Hooks.CreateContainer = append(config.Hooks.CreateContainer, h.ToOCI())
|
||||
case "startContainer":
|
||||
config.Hooks.StartContainer = append(config.Hooks.StartContainer, h.ToOCI())
|
||||
case "poststart":
|
||||
config.Hooks.Poststart = append(config.Hooks.Poststart, h.ToOCI())
|
||||
case "poststop":
|
||||
config.Hooks.Poststop = append(config.Hooks.Poststop, h.ToOCI())
|
||||
default:
|
||||
fmt.Printf("CDI: Unknown hook %q\n", h.HookName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec Hook for this Hook.
|
||||
func (h *Hook) ToOCI() spec.Hook {
|
||||
return spec.Hook{
|
||||
Path: h.Path,
|
||||
Args: h.Args,
|
||||
Env: h.Env,
|
||||
Timeout: h.Timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec Mount for this Mount.
|
||||
func (m *Mount) ToOCI() spec.Mount {
|
||||
return spec.Mount{
|
||||
Source: m.HostPath,
|
||||
Destination: m.ContainerPath,
|
||||
Options: m.Options,
|
||||
Type: m.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.
|
||||
func (d *DeviceNode) ToOCI() spec.LinuxDevice {
|
||||
return spec.LinuxDevice{
|
||||
Path: d.Path,
|
||||
Type: d.Type,
|
||||
Major: d.Major,
|
||||
Minor: d.Minor,
|
||||
FileMode: d.FileMode,
|
||||
UID: d.UID,
|
||||
GID: d.GID,
|
||||
}
|
||||
}
|
||||
191
vendor/github.com/opencontainers/runc/LICENSE
generated
vendored
191
vendor/github.com/opencontainers/runc/LICENSE
generated
vendored
@@ -1,191 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Docker, Inc.
|
||||
|
||||
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.
|
||||
17
vendor/github.com/opencontainers/runc/NOTICE
generated
vendored
17
vendor/github.com/opencontainers/runc/NOTICE
generated
vendored
@@ -1,17 +0,0 @@
|
||||
runc
|
||||
|
||||
Copyright 2012-2015 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (http://www.docker.com).
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
|
||||
Use and transfer of Docker may be subject to certain restrictions by the
|
||||
United States and other governments.
|
||||
It is your responsibility to ensure that your use and/or transfer does not
|
||||
violate applicable laws.
|
||||
|
||||
For more information, please see http://www.bis.doc.gov
|
||||
|
||||
See also http://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
||||
174
vendor/github.com/opencontainers/runc/libcontainer/devices/device.go
generated
vendored
174
vendor/github.com/opencontainers/runc/libcontainer/devices/device.go
generated
vendored
@@ -1,174 +0,0 @@
|
||||
package devices
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
Wildcard = -1
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
Rule
|
||||
|
||||
// Path to the device.
|
||||
Path string `json:"path"`
|
||||
|
||||
// FileMode permission bits for the device.
|
||||
FileMode os.FileMode `json:"file_mode"`
|
||||
|
||||
// Uid of the device.
|
||||
Uid uint32 `json:"uid"`
|
||||
|
||||
// Gid of the device.
|
||||
Gid uint32 `json:"gid"`
|
||||
}
|
||||
|
||||
// Permissions is a cgroupv1-style string to represent device access. It
|
||||
// has to be a string for backward compatibility reasons, hence why it has
|
||||
// methods to do set operations.
|
||||
type Permissions string
|
||||
|
||||
const (
|
||||
deviceRead uint = (1 << iota)
|
||||
deviceWrite
|
||||
deviceMknod
|
||||
)
|
||||
|
||||
func (p Permissions) toSet() uint {
|
||||
var set uint
|
||||
for _, perm := range p {
|
||||
switch perm {
|
||||
case 'r':
|
||||
set |= deviceRead
|
||||
case 'w':
|
||||
set |= deviceWrite
|
||||
case 'm':
|
||||
set |= deviceMknod
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func fromSet(set uint) Permissions {
|
||||
var perm string
|
||||
if set&deviceRead == deviceRead {
|
||||
perm += "r"
|
||||
}
|
||||
if set&deviceWrite == deviceWrite {
|
||||
perm += "w"
|
||||
}
|
||||
if set&deviceMknod == deviceMknod {
|
||||
perm += "m"
|
||||
}
|
||||
return Permissions(perm)
|
||||
}
|
||||
|
||||
// Union returns the union of the two sets of Permissions.
|
||||
func (p Permissions) Union(o Permissions) Permissions {
|
||||
lhs := p.toSet()
|
||||
rhs := o.toSet()
|
||||
return fromSet(lhs | rhs)
|
||||
}
|
||||
|
||||
// Difference returns the set difference of the two sets of Permissions.
|
||||
// In set notation, A.Difference(B) gives you A\B.
|
||||
func (p Permissions) Difference(o Permissions) Permissions {
|
||||
lhs := p.toSet()
|
||||
rhs := o.toSet()
|
||||
return fromSet(lhs &^ rhs)
|
||||
}
|
||||
|
||||
// Intersection computes the intersection of the two sets of Permissions.
|
||||
func (p Permissions) Intersection(o Permissions) Permissions {
|
||||
lhs := p.toSet()
|
||||
rhs := o.toSet()
|
||||
return fromSet(lhs & rhs)
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the set of permissions in a Permissions is
|
||||
// empty.
|
||||
func (p Permissions) IsEmpty() bool {
|
||||
return p == Permissions("")
|
||||
}
|
||||
|
||||
// IsValid returns whether the set of permissions is a subset of valid
|
||||
// permissions (namely, {r,w,m}).
|
||||
func (p Permissions) IsValid() bool {
|
||||
return p == fromSet(p.toSet())
|
||||
}
|
||||
|
||||
type Type rune
|
||||
|
||||
const (
|
||||
WildcardDevice Type = 'a'
|
||||
BlockDevice Type = 'b'
|
||||
CharDevice Type = 'c' // or 'u'
|
||||
FifoDevice Type = 'p'
|
||||
)
|
||||
|
||||
func (t Type) IsValid() bool {
|
||||
switch t {
|
||||
case WildcardDevice, BlockDevice, CharDevice, FifoDevice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) CanMknod() bool {
|
||||
switch t {
|
||||
case BlockDevice, CharDevice, FifoDevice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) CanCgroup() bool {
|
||||
switch t {
|
||||
case WildcardDevice, BlockDevice, CharDevice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
// Type of device ('c' for char, 'b' for block). If set to 'a', this rule
|
||||
// acts as a wildcard and all fields other than Allow are ignored.
|
||||
Type Type `json:"type"`
|
||||
|
||||
// Major is the device's major number.
|
||||
Major int64 `json:"major"`
|
||||
|
||||
// Minor is the device's minor number.
|
||||
Minor int64 `json:"minor"`
|
||||
|
||||
// Permissions is the set of permissions that this rule applies to (in the
|
||||
// cgroupv1 format -- any combination of "rwm").
|
||||
Permissions Permissions `json:"permissions"`
|
||||
|
||||
// Allow specifies whether this rule is allowed.
|
||||
Allow bool `json:"allow"`
|
||||
}
|
||||
|
||||
func (d *Rule) CgroupString() string {
|
||||
var (
|
||||
major = strconv.FormatInt(d.Major, 10)
|
||||
minor = strconv.FormatInt(d.Minor, 10)
|
||||
)
|
||||
if d.Major == Wildcard {
|
||||
major = "*"
|
||||
}
|
||||
if d.Minor == Wildcard {
|
||||
minor = "*"
|
||||
}
|
||||
return fmt.Sprintf("%c %s:%s %s", d.Type, major, minor, d.Permissions)
|
||||
}
|
||||
|
||||
func (d *Rule) Mkdev() (uint64, error) {
|
||||
return mkDev(d)
|
||||
}
|
||||
120
vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go
generated
vendored
120
vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go
generated
vendored
@@ -1,120 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package devices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ErrNotADevice denotes that a file is not a valid linux device.
|
||||
var ErrNotADevice = errors.New("not a device node")
|
||||
|
||||
// Testing dependencies
|
||||
var (
|
||||
unixLstat = unix.Lstat
|
||||
osReadDir = os.ReadDir
|
||||
)
|
||||
|
||||
func mkDev(d *Rule) (uint64, error) {
|
||||
if d.Major == Wildcard || d.Minor == Wildcard {
|
||||
return 0, errors.New("cannot mkdev() device with wildcards")
|
||||
}
|
||||
return unix.Mkdev(uint32(d.Major), uint32(d.Minor)), nil
|
||||
}
|
||||
|
||||
// DeviceFromPath takes the path to a device and its cgroup_permissions (which
|
||||
// cannot be easily queried) to look up the information about a linux device
|
||||
// and returns that information as a Device struct.
|
||||
func DeviceFromPath(path, permissions string) (*Device, error) {
|
||||
var stat unix.Stat_t
|
||||
err := unixLstat(path, &stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
devType Type
|
||||
mode = stat.Mode
|
||||
devNumber = uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS.
|
||||
major = unix.Major(devNumber)
|
||||
minor = unix.Minor(devNumber)
|
||||
)
|
||||
switch mode & unix.S_IFMT {
|
||||
case unix.S_IFBLK:
|
||||
devType = BlockDevice
|
||||
case unix.S_IFCHR:
|
||||
devType = CharDevice
|
||||
case unix.S_IFIFO:
|
||||
devType = FifoDevice
|
||||
default:
|
||||
return nil, ErrNotADevice
|
||||
}
|
||||
return &Device{
|
||||
Rule: Rule{
|
||||
Type: devType,
|
||||
Major: int64(major),
|
||||
Minor: int64(minor),
|
||||
Permissions: Permissions(permissions),
|
||||
},
|
||||
Path: path,
|
||||
FileMode: os.FileMode(mode &^ unix.S_IFMT),
|
||||
Uid: stat.Uid,
|
||||
Gid: stat.Gid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HostDevices returns all devices that can be found under /dev directory.
|
||||
func HostDevices() ([]*Device, error) {
|
||||
return GetDevices("/dev")
|
||||
}
|
||||
|
||||
// GetDevices recursively traverses a directory specified by path
|
||||
// and returns all devices found there.
|
||||
func GetDevices(path string) ([]*Device, error) {
|
||||
files, err := osReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []*Device
|
||||
for _, f := range files {
|
||||
switch {
|
||||
case f.IsDir():
|
||||
switch f.Name() {
|
||||
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
|
||||
// ".udev" added to address https://github.com/opencontainers/runc/issues/2093
|
||||
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev":
|
||||
continue
|
||||
default:
|
||||
sub, err := GetDevices(filepath.Join(path, f.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out = append(out, sub...)
|
||||
continue
|
||||
}
|
||||
case f.Name() == "console":
|
||||
continue
|
||||
}
|
||||
device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNotADevice) {
|
||||
continue
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if device.Type == FifoDevice {
|
||||
continue
|
||||
}
|
||||
out = append(out, device)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
88
vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
generated
vendored
88
vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
generated
vendored
@@ -33,6 +33,34 @@ type Spec struct {
|
||||
ZOS *ZOS `json:"zos,omitempty" platform:"zos"`
|
||||
}
|
||||
|
||||
// Scheduler represents the scheduling attributes for a process. It is based on
|
||||
// the Linux sched_setattr(2) syscall.
|
||||
type Scheduler struct {
|
||||
// Policy represents the scheduling policy (e.g., SCHED_FIFO, SCHED_RR, SCHED_OTHER).
|
||||
Policy LinuxSchedulerPolicy `json:"policy"`
|
||||
|
||||
// Nice is the nice value for the process, which affects its priority.
|
||||
Nice int32 `json:"nice,omitempty"`
|
||||
|
||||
// Priority represents the static priority of the process.
|
||||
Priority int32 `json:"priority,omitempty"`
|
||||
|
||||
// Flags is an array of scheduling flags.
|
||||
Flags []LinuxSchedulerFlag `json:"flags,omitempty"`
|
||||
|
||||
// The following ones are used by the DEADLINE scheduler.
|
||||
|
||||
// Runtime is the amount of time in nanoseconds during which the process
|
||||
// is allowed to run in a given period.
|
||||
Runtime uint64 `json:"runtime,omitempty"`
|
||||
|
||||
// Deadline is the absolute deadline for the process to complete its execution.
|
||||
Deadline uint64 `json:"deadline,omitempty"`
|
||||
|
||||
// Period is the length of the period in nanoseconds used for determining the process runtime.
|
||||
Period uint64 `json:"period,omitempty"`
|
||||
}
|
||||
|
||||
// Process contains information to start a specific application inside the container.
|
||||
type Process struct {
|
||||
// Terminal creates an interactive terminal for the container.
|
||||
@@ -60,8 +88,12 @@ type Process struct {
|
||||
ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"`
|
||||
// Specify an oom_score_adj for the container.
|
||||
OOMScoreAdj *int `json:"oomScoreAdj,omitempty" platform:"linux"`
|
||||
// Scheduler specifies the scheduling attributes for a process
|
||||
Scheduler *Scheduler `json:"scheduler,omitempty" platform:"linux"`
|
||||
// SelinuxLabel specifies the selinux context that the container process is run as.
|
||||
SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"`
|
||||
// IOPriority contains the I/O priority settings for the cgroup.
|
||||
IOPriority *LinuxIOPriority `json:"ioPriority,omitempty" platform:"linux"`
|
||||
}
|
||||
|
||||
// LinuxCapabilities specifies the list of allowed capabilities that are kept for a process.
|
||||
@@ -79,6 +111,22 @@ type LinuxCapabilities struct {
|
||||
Ambient []string `json:"ambient,omitempty" platform:"linux"`
|
||||
}
|
||||
|
||||
// IOPriority represents I/O priority settings for the container's processes within the process group.
|
||||
type LinuxIOPriority struct {
|
||||
Class IOPriorityClass `json:"class"`
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
|
||||
// IOPriorityClass represents an I/O scheduling class.
|
||||
type IOPriorityClass string
|
||||
|
||||
// Possible values for IOPriorityClass.
|
||||
const (
|
||||
IOPRIO_CLASS_RT IOPriorityClass = "IOPRIO_CLASS_RT"
|
||||
IOPRIO_CLASS_BE IOPriorityClass = "IOPRIO_CLASS_BE"
|
||||
IOPRIO_CLASS_IDLE IOPriorityClass = "IOPRIO_CLASS_IDLE"
|
||||
)
|
||||
|
||||
// Box specifies dimensions of a rectangle. Used for specifying the size of a console.
|
||||
type Box struct {
|
||||
// Height is the vertical dimension of a box.
|
||||
@@ -789,3 +837,43 @@ type ZOSDevice struct {
|
||||
// Gid of the device.
|
||||
GID *uint32 `json:"gid,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxSchedulerPolicy represents different scheduling policies used with the Linux Scheduler
|
||||
type LinuxSchedulerPolicy string
|
||||
|
||||
const (
|
||||
// SchedOther is the default scheduling policy
|
||||
SchedOther LinuxSchedulerPolicy = "SCHED_OTHER"
|
||||
// SchedFIFO is the First-In-First-Out scheduling policy
|
||||
SchedFIFO LinuxSchedulerPolicy = "SCHED_FIFO"
|
||||
// SchedRR is the Round-Robin scheduling policy
|
||||
SchedRR LinuxSchedulerPolicy = "SCHED_RR"
|
||||
// SchedBatch is the Batch scheduling policy
|
||||
SchedBatch LinuxSchedulerPolicy = "SCHED_BATCH"
|
||||
// SchedISO is the Isolation scheduling policy
|
||||
SchedISO LinuxSchedulerPolicy = "SCHED_ISO"
|
||||
// SchedIdle is the Idle scheduling policy
|
||||
SchedIdle LinuxSchedulerPolicy = "SCHED_IDLE"
|
||||
// SchedDeadline is the Deadline scheduling policy
|
||||
SchedDeadline LinuxSchedulerPolicy = "SCHED_DEADLINE"
|
||||
)
|
||||
|
||||
// LinuxSchedulerFlag represents the flags used by the Linux Scheduler.
|
||||
type LinuxSchedulerFlag string
|
||||
|
||||
const (
|
||||
// SchedFlagResetOnFork represents the reset on fork scheduling flag
|
||||
SchedFlagResetOnFork LinuxSchedulerFlag = "SCHED_FLAG_RESET_ON_FORK"
|
||||
// SchedFlagReclaim represents the reclaim scheduling flag
|
||||
SchedFlagReclaim LinuxSchedulerFlag = "SCHED_FLAG_RECLAIM"
|
||||
// SchedFlagDLOverrun represents the deadline overrun scheduling flag
|
||||
SchedFlagDLOverrun LinuxSchedulerFlag = "SCHED_FLAG_DL_OVERRUN"
|
||||
// SchedFlagKeepPolicy represents the keep policy scheduling flag
|
||||
SchedFlagKeepPolicy LinuxSchedulerFlag = "SCHED_FLAG_KEEP_POLICY"
|
||||
// SchedFlagKeepParams represents the keep parameters scheduling flag
|
||||
SchedFlagKeepParams LinuxSchedulerFlag = "SCHED_FLAG_KEEP_PARAMS"
|
||||
// SchedFlagUtilClampMin represents the utilization clamp minimum scheduling flag
|
||||
SchedFlagUtilClampMin LinuxSchedulerFlag = "SCHED_FLAG_UTIL_CLAMP_MIN"
|
||||
// SchedFlagUtilClampMin represents the utilization clamp maximum scheduling flag
|
||||
SchedFlagUtilClampMax LinuxSchedulerFlag = "SCHED_FLAG_UTIL_CLAMP_MAX"
|
||||
)
|
||||
|
||||
2
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
2
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
@@ -11,7 +11,7 @@ const (
|
||||
VersionPatch = 0
|
||||
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev = "-rc.2"
|
||||
VersionDev = ""
|
||||
)
|
||||
|
||||
// Version is the specification version that the package types support.
|
||||
|
||||
Reference in New Issue
Block a user