|
4 | 4 | // For more information about Container Device Interface, please refer to
|
5 | 5 | // https://github.com/container-orchestrated-devices/container-device-interface
|
6 | 6 | //
|
7 |
| -// Container Device Interface |
| 7 | +// # Container Device Interface |
8 | 8 | //
|
9 | 9 | // Container Device Interface, or CDI for short, provides comprehensive
|
10 | 10 | // third party device support for container runtimes. CDI uses vendor
|
|
29 | 29 | // the vast majority of CDI consumers need. The API should be usable both
|
30 | 30 | // by OCI runtime clients and runtime implementations.
|
31 | 31 | //
|
32 |
| -// CDI Registry |
| 32 | +// # CDI Registry |
33 | 33 | //
|
34 | 34 | // The primary interface to interact with CDI devices is the Registry. It
|
35 | 35 | // is essentially a cache of all Specs and devices discovered in standard
|
36 | 36 | // CDI directories on the host. The registry has two main functionality,
|
37 | 37 | // injecting devices into an OCI Spec and refreshing the cache of CDI
|
38 | 38 | // Specs and devices.
|
39 | 39 | //
|
40 |
| -// Device Injection |
| 40 | +// # Device Injection |
41 | 41 | //
|
42 | 42 | // Using the Registry one can inject CDI devices into a container with code
|
43 | 43 | // similar to the following snippet:
|
44 | 44 | //
|
45 |
| -// import ( |
46 |
| -// "fmt" |
47 |
| -// "strings" |
| 45 | +// import ( |
| 46 | +// "fmt" |
| 47 | +// "strings" |
48 | 48 | //
|
49 |
| -// log "github.com/sirupsen/logrus" |
| 49 | +// log "github.com/sirupsen/logrus" |
50 | 50 | //
|
51 |
| -// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
52 |
| -// oci "github.com/opencontainers/runtime-spec/specs-go" |
53 |
| -// ) |
| 51 | +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
| 52 | +// oci "github.com/opencontainers/runtime-spec/specs-go" |
| 53 | +// ) |
54 | 54 | //
|
55 |
| -// func injectCDIDevices(spec *oci.Spec, devices []string) error { |
56 |
| -// log.Debug("pristine OCI Spec: %s", dumpSpec(spec)) |
| 55 | +// func injectCDIDevices(spec *oci.Spec, devices []string) error { |
| 56 | +// log.Debug("pristine OCI Spec: %s", dumpSpec(spec)) |
57 | 57 | //
|
58 |
| -// unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices) |
59 |
| -// if err != nil { |
60 |
| -// return fmt.Errorf("CDI device injection failed: %w", err) |
61 |
| -// } |
| 58 | +// unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices) |
| 59 | +// if err != nil { |
| 60 | +// return fmt.Errorf("CDI device injection failed: %w", err) |
| 61 | +// } |
62 | 62 | //
|
63 |
| -// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec)) |
64 |
| -// return nil |
65 |
| -// } |
| 63 | +// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec)) |
| 64 | +// return nil |
| 65 | +// } |
66 | 66 | //
|
67 |
| -// Cache Refresh |
| 67 | +// # Cache Refresh |
68 | 68 | //
|
69 | 69 | // By default the CDI Spec cache monitors the configured Spec directories
|
70 | 70 | // and automatically refreshes itself when necessary. This behavior can be
|
|
85 | 85 | // CDI Spec cache is up to date before performing device injection.
|
86 | 86 | // A code snippet similar to the following accmplishes that:
|
87 | 87 | //
|
88 |
| -// import ( |
89 |
| -// "fmt" |
90 |
| -// "strings" |
| 88 | +// import ( |
| 89 | +// "fmt" |
| 90 | +// "strings" |
91 | 91 | //
|
92 |
| -// log "github.com/sirupsen/logrus" |
| 92 | +// log "github.com/sirupsen/logrus" |
93 | 93 | //
|
94 |
| -// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
95 |
| -// oci "github.com/opencontainers/runtime-spec/specs-go" |
96 |
| -// ) |
| 94 | +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
| 95 | +// oci "github.com/opencontainers/runtime-spec/specs-go" |
| 96 | +// ) |
97 | 97 | //
|
98 |
| -// func injectCDIDevices(spec *oci.Spec, devices []string) error { |
99 |
| -// registry := cdi.GetRegistry() |
| 98 | +// func injectCDIDevices(spec *oci.Spec, devices []string) error { |
| 99 | +// registry := cdi.GetRegistry() |
100 | 100 | //
|
101 |
| -// if err := registry.Refresh(); err != nil { |
102 |
| -// // Note: |
103 |
| -// // It is up to the implementation to decide whether |
104 |
| -// // to abort injection on errors. A failed Refresh() |
105 |
| -// // does not necessarily render the registry unusable. |
106 |
| -// // For instance, a parse error in a Spec file for |
107 |
| -// // vendor A does not have any effect on devices of |
108 |
| -// // vendor B... |
109 |
| -// log.Warnf("pre-injection Refresh() failed: %v", err) |
110 |
| -// } |
| 101 | +// if err := registry.Refresh(); err != nil { |
| 102 | +// // Note: |
| 103 | +// // It is up to the implementation to decide whether |
| 104 | +// // to abort injection on errors. A failed Refresh() |
| 105 | +// // does not necessarily render the registry unusable. |
| 106 | +// // For instance, a parse error in a Spec file for |
| 107 | +// // vendor A does not have any effect on devices of |
| 108 | +// // vendor B... |
| 109 | +// log.Warnf("pre-injection Refresh() failed: %v", err) |
| 110 | +// } |
111 | 111 | //
|
112 |
| -// log.Debug("pristine OCI Spec: %s", dumpSpec(spec)) |
| 112 | +// log.Debug("pristine OCI Spec: %s", dumpSpec(spec)) |
113 | 113 | //
|
114 |
| -// unresolved, err := registry.InjectDevices(spec, devices) |
115 |
| -// if err != nil { |
116 |
| -// return fmt.Errorf("CDI device injection failed: %w", err) |
117 |
| -// } |
| 114 | +// unresolved, err := registry.InjectDevices(spec, devices) |
| 115 | +// if err != nil { |
| 116 | +// return fmt.Errorf("CDI device injection failed: %w", err) |
| 117 | +// } |
118 | 118 | //
|
119 |
| -// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec)) |
120 |
| -// return nil |
121 |
| -// } |
| 119 | +// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec)) |
| 120 | +// return nil |
| 121 | +// } |
122 | 122 | //
|
123 |
| -// Generated Spec Files, Multiple Directories, Device Precedence |
| 123 | +// # Generated Spec Files, Multiple Directories, Device Precedence |
124 | 124 | //
|
125 | 125 | // It is often necessary to generate Spec files dynamically. On some
|
126 | 126 | // systems the available or usable set of CDI devices might change
|
|
149 | 149 | // '/etc/cdi' while all the dynamically generated Spec files, transient
|
150 | 150 | // or other, go into '/var/run/cdi'.
|
151 | 151 | //
|
152 |
| -// Spec File Generation |
| 152 | +// # Spec File Generation |
153 | 153 | //
|
154 | 154 | // CDI offers two functions for writing and removing dynamically generated
|
155 | 155 | // Specs from CDI Spec directories. These functions, WriteSpec() and
|
|
169 | 169 | // code snippet similar to the following:
|
170 | 170 | //
|
171 | 171 | // import (
|
172 |
| -// "fmt" |
173 |
| -// ... |
174 |
| -// "github.com/container-orchestrated-devices/container-device-interface/specs-go" |
175 |
| -// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
| 172 | +// |
| 173 | +// "fmt" |
| 174 | +// ... |
| 175 | +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" |
| 176 | +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
| 177 | +// |
176 | 178 | // )
|
177 | 179 | //
|
178 |
| -// func generateDeviceSpecs() error { |
179 |
| -// registry := cdi.GetRegistry() |
180 |
| -// spec := &specs.Spec{ |
181 |
| -// Version: specs.CurrentVersion, |
182 |
| -// Kind: vendor+"/"+class, |
183 |
| -// } |
| 180 | +// func generateDeviceSpecs() error { |
| 181 | +// registry := cdi.GetRegistry() |
| 182 | +// spec := &specs.Spec{ |
| 183 | +// Version: specs.CurrentVersion, |
| 184 | +// Kind: vendor+"/"+class, |
| 185 | +// } |
184 | 186 | //
|
185 |
| -// for _, dev := range enumerateDevices() { |
186 |
| -// spec.Devices = append(spec.Devices, specs.Device{ |
187 |
| -// Name: dev.Name, |
188 |
| -// ContainerEdits: getContainerEditsForDevice(dev), |
189 |
| -// }) |
190 |
| -// } |
| 187 | +// for _, dev := range enumerateDevices() { |
| 188 | +// spec.Devices = append(spec.Devices, specs.Device{ |
| 189 | +// Name: dev.Name, |
| 190 | +// ContainerEdits: getContainerEditsForDevice(dev), |
| 191 | +// }) |
| 192 | +// } |
191 | 193 | //
|
192 |
| -// specName, err := cdi.GenerateNameForSpec(spec) |
193 |
| -// if err != nil { |
194 |
| -// return fmt.Errorf("failed to generate Spec name: %w", err) |
195 |
| -// } |
| 194 | +// specName, err := cdi.GenerateNameForSpec(spec) |
| 195 | +// if err != nil { |
| 196 | +// return fmt.Errorf("failed to generate Spec name: %w", err) |
| 197 | +// } |
196 | 198 | //
|
197 |
| -// return registry.SpecDB().WriteSpec(spec, specName) |
198 |
| -// } |
| 199 | +// return registry.SpecDB().WriteSpec(spec, specName) |
| 200 | +// } |
199 | 201 | //
|
200 | 202 | // Similarly, generating and later cleaning up transient Spec files can be
|
201 | 203 | // done with code fragments similar to the following. These transient Spec
|
|
204 | 206 | // and removed once that container is removed.
|
205 | 207 | //
|
206 | 208 | // import (
|
207 |
| -// "fmt" |
208 |
| -// ... |
209 |
| -// "github.com/container-orchestrated-devices/container-device-interface/specs-go" |
210 |
| -// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
| 209 | +// |
| 210 | +// "fmt" |
| 211 | +// ... |
| 212 | +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" |
| 213 | +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" |
| 214 | +// |
211 | 215 | // )
|
212 | 216 | //
|
213 |
| -// func generateTransientSpec(ctr Container) error { |
214 |
| -// registry := cdi.GetRegistry() |
215 |
| -// devices := getContainerDevs(ctr, vendor, class) |
216 |
| -// spec := &specs.Spec{ |
217 |
| -// Version: specs.CurrentVersion, |
218 |
| -// Kind: vendor+"/"+class, |
219 |
| -// } |
220 |
| -// |
221 |
| -// for _, dev := range devices { |
222 |
| -// spec.Devices = append(spec.Devices, specs.Device{ |
223 |
| -// // the generated name needs to be unique within the |
224 |
| -// // vendor/class domain on the host/node. |
225 |
| -// Name: generateUniqueDevName(dev, ctr), |
226 |
| -// ContainerEdits: getEditsForContainer(dev), |
227 |
| -// }) |
228 |
| -// } |
229 |
| -// |
230 |
| -// // transientID is expected to guarantee that the Spec file name |
231 |
| -// // generated using <vendor, class, transientID> is unique within |
232 |
| -// // the host/node. If more than one device is allocated with the |
233 |
| -// // same vendor/class domain, either all generated Spec entries |
234 |
| -// // should go to a single Spec file (like in this sample snippet), |
235 |
| -// // or transientID should be unique for each generated Spec file. |
236 |
| -// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) |
237 |
| -// specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID) |
238 |
| -// if err != nil { |
239 |
| -// return fmt.Errorf("failed to generate Spec name: %w", err) |
240 |
| -// } |
241 |
| -// |
242 |
| -// return registry.SpecDB().WriteSpec(spec, specName) |
243 |
| -// } |
244 |
| -// |
245 |
| -// func removeTransientSpec(ctr Container) error { |
246 |
| -// registry := cdi.GetRegistry() |
247 |
| -// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) |
248 |
| -// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID) |
249 |
| -// |
250 |
| -// return registry.SpecDB().RemoveSpec(specName) |
251 |
| -// } |
252 |
| -// |
253 |
| -// CDI Spec Validation |
| 217 | +// func generateTransientSpec(ctr Container) error { |
| 218 | +// registry := cdi.GetRegistry() |
| 219 | +// devices := getContainerDevs(ctr, vendor, class) |
| 220 | +// spec := &specs.Spec{ |
| 221 | +// Version: specs.CurrentVersion, |
| 222 | +// Kind: vendor+"/"+class, |
| 223 | +// } |
| 224 | +// |
| 225 | +// for _, dev := range devices { |
| 226 | +// spec.Devices = append(spec.Devices, specs.Device{ |
| 227 | +// // the generated name needs to be unique within the |
| 228 | +// // vendor/class domain on the host/node. |
| 229 | +// Name: generateUniqueDevName(dev, ctr), |
| 230 | +// ContainerEdits: getEditsForContainer(dev), |
| 231 | +// }) |
| 232 | +// } |
| 233 | +// |
| 234 | +// // transientID is expected to guarantee that the Spec file name |
| 235 | +// // generated using <vendor, class, transientID> is unique within |
| 236 | +// // the host/node. If more than one device is allocated with the |
| 237 | +// // same vendor/class domain, either all generated Spec entries |
| 238 | +// // should go to a single Spec file (like in this sample snippet), |
| 239 | +// // or transientID should be unique for each generated Spec file. |
| 240 | +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) |
| 241 | +// specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID) |
| 242 | +// if err != nil { |
| 243 | +// return fmt.Errorf("failed to generate Spec name: %w", err) |
| 244 | +// } |
| 245 | +// |
| 246 | +// return registry.SpecDB().WriteSpec(spec, specName) |
| 247 | +// } |
| 248 | +// |
| 249 | +// func removeTransientSpec(ctr Container) error { |
| 250 | +// registry := cdi.GetRegistry() |
| 251 | +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) |
| 252 | +// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID) |
| 253 | +// |
| 254 | +// return registry.SpecDB().RemoveSpec(specName) |
| 255 | +// } |
| 256 | +// |
| 257 | +// # CDI Spec Validation |
254 | 258 | //
|
255 | 259 | // This package performs both syntactic and semantic validation of CDI
|
256 | 260 | // Spec file data when a Spec file is loaded via the registry or using
|
|
0 commit comments