|
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