123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- /*
- 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
- }
|