Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 471811d

Browse files
committedFeb 4, 2025
Move annotation generation to producer package
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent e5783c3 commit 471811d

File tree

3 files changed

+453
-66
lines changed

3 files changed

+453
-66
lines changed
 

‎api/producer/annotations.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,101 @@
1717
package producer
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"strings"
2223

2324
"tags.cncf.io/container-device-interface/api/producer/k8s"
2425
)
2526

27+
const (
28+
// AnnotationPrefix is the prefix for CDI container annotation keys.
29+
AnnotationPrefix = "cdi.k8s.io/"
30+
)
31+
32+
// UpdateAnnotations updates annotations with a plugin-specific CDI device
33+
// injection request for the given devices. Upon any error a non-nil error
34+
// is returned and annotations are left intact. By convention plugin should
35+
// be in the format of "vendor.device-type".
36+
func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, devices []string) (map[string]string, error) {
37+
key, err := AnnotationKey(plugin, deviceID)
38+
if err != nil {
39+
return annotations, fmt.Errorf("CDI annotation failed: %w", err)
40+
}
41+
if _, ok := annotations[key]; ok {
42+
return annotations, fmt.Errorf("CDI annotation failed, key %q used", key)
43+
}
44+
value, err := AnnotationValue(devices)
45+
if err != nil {
46+
return annotations, fmt.Errorf("CDI annotation failed: %w", err)
47+
}
48+
49+
if annotations == nil {
50+
annotations = make(map[string]string)
51+
}
52+
annotations[key] = value
53+
54+
return annotations, nil
55+
}
56+
57+
// AnnotationKey returns a unique annotation key for an device allocation
58+
// by a K8s device plugin. pluginName should be in the format of
59+
// "vendor.device-type". deviceID is the ID of the device the plugin is
60+
// allocating. It is used to make sure that the generated key is unique
61+
// even if multiple allocations by a single plugin needs to be annotated.
62+
func AnnotationKey(pluginName, deviceID string) (string, error) {
63+
const maxNameLen = 63
64+
65+
if pluginName == "" {
66+
return "", errors.New("invalid plugin name, empty")
67+
}
68+
if deviceID == "" {
69+
return "", errors.New("invalid deviceID, empty")
70+
}
71+
72+
name := pluginName + "_" + strings.ReplaceAll(deviceID, "/", "_")
73+
74+
if len(name) > maxNameLen {
75+
return "", fmt.Errorf("invalid plugin+deviceID %q, too long", name)
76+
}
77+
78+
if c := rune(name[0]); !isAlphaNumeric(c) {
79+
return "", fmt.Errorf("invalid name %q, first '%c' should be alphanumeric",
80+
name, c)
81+
}
82+
if len(name) > 2 {
83+
for _, c := range name[1 : len(name)-1] {
84+
switch {
85+
case isAlphaNumeric(c):
86+
case c == '_' || c == '-' || c == '.':
87+
default:
88+
return "", fmt.Errorf("invalid name %q, invalid character '%c'",
89+
name, c)
90+
}
91+
}
92+
}
93+
if c := rune(name[len(name)-1]); !isAlphaNumeric(c) {
94+
return "", fmt.Errorf("invalid name %q, last '%c' should be alphanumeric",
95+
name, c)
96+
}
97+
98+
return AnnotationPrefix + name, nil
99+
}
100+
101+
// AnnotationValue returns an annotation value for the given devices.
102+
func AnnotationValue(devices []string) (string, error) {
103+
value, sep := "", ""
104+
for _, d := range devices {
105+
if err := ValidateQualifiedName(d); err != nil {
106+
return "", err
107+
}
108+
value += sep + d
109+
sep = ","
110+
}
111+
112+
return value, nil
113+
}
114+
26115
// ValidateSpecAnnotations checks whether spec annotations are valid.
27116
func ValidateSpecAnnotations(name string, any interface{}) error {
28117
if any == nil {
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.