cdi.go 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package daemon
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
  6. "github.com/containerd/containerd/log"
  7. "github.com/docker/docker/errdefs"
  8. "github.com/hashicorp/go-multierror"
  9. specs "github.com/opencontainers/runtime-spec/specs-go"
  10. "github.com/pkg/errors"
  11. )
  12. type cdiHandler struct {
  13. registry *cdi.Cache
  14. }
  15. // RegisterCDIDriver registers the CDI device driver.
  16. // The driver injects CDI devices into an incoming OCI spec and is called for DeviceRequests associated with CDI devices.
  17. func RegisterCDIDriver(opts ...cdi.Option) {
  18. cache, err := cdi.NewCache(opts...)
  19. if err != nil {
  20. log.G(context.TODO()).WithError(err).Error("CDI registry initialization failed")
  21. // We create a spec updater that always returns an error.
  22. // This error will be returned only when a CDI device is requested.
  23. // This ensures that daemon startup is not blocked by a CDI registry initialization failure.
  24. errorOnUpdateSpec := func(s *specs.Spec, dev *deviceInstance) error {
  25. return fmt.Errorf("CDI device injection failed due to registry initialization failure: %w", err)
  26. }
  27. driver := &deviceDriver{
  28. updateSpec: errorOnUpdateSpec,
  29. }
  30. registerDeviceDriver("cdi", driver)
  31. return
  32. }
  33. // We construct a spec updates that injects CDI devices into the OCI spec using the initialized registry.
  34. c := &cdiHandler{
  35. registry: cache,
  36. }
  37. driver := &deviceDriver{
  38. updateSpec: c.injectCDIDevices,
  39. }
  40. registerDeviceDriver("cdi", driver)
  41. }
  42. // injectCDIDevices injects a set of CDI devices into the specified OCI specification.
  43. func (c *cdiHandler) injectCDIDevices(s *specs.Spec, dev *deviceInstance) error {
  44. if dev.req.Count != 0 {
  45. return errdefs.InvalidParameter(errors.New("unexpected count in CDI device request"))
  46. }
  47. if len(dev.req.Options) > 0 {
  48. return errdefs.InvalidParameter(errors.New("unexpected options in CDI device request"))
  49. }
  50. cdiDeviceNames := dev.req.DeviceIDs
  51. if len(cdiDeviceNames) == 0 {
  52. return nil
  53. }
  54. _, err := c.registry.InjectDevices(s, cdiDeviceNames...)
  55. if err != nil {
  56. if rerrs := c.getErrors(); rerrs != nil {
  57. // We log the errors that may have been generated while refreshing the CDI registry.
  58. // These may be due to malformed specifications or device name conflicts that could be
  59. // the cause of an injection failure.
  60. log.G(context.TODO()).WithError(rerrs).Warning("Refreshing the CDI registry generated errors")
  61. }
  62. return fmt.Errorf("CDI device injection failed: %w", err)
  63. }
  64. return nil
  65. }
  66. // getErrors returns a single error representation of errors that may have occurred while refreshing the CDI registry.
  67. func (c *cdiHandler) getErrors() error {
  68. errors := c.registry.GetErrors()
  69. var err *multierror.Error
  70. for _, errs := range errors {
  71. err = multierror.Append(err, errs...)
  72. }
  73. return err.ErrorOrNil()
  74. }