package events import ( "context" "path/filepath" "strings" "time" "github.com/containerd/containerd/containers" "edge-infra.dev/pkg/lib/kernel/devices" "edge-infra.dev/pkg/lib/kernel/udev" cc "edge-infra.dev/pkg/sds/devices/agent/common" "edge-infra.dev/pkg/sds/devices/class" dsv1 "edge-infra.dev/pkg/sds/devices/k8s/apis/v1" "edge-infra.dev/pkg/sds/devices/logger" ) type DeviceEvent interface { Containers() map[string]*containers.Container PosthookFunc() func(context.Context) Timestamp() time.Time } // Base class for a device event type event struct { // list of new container ids containers map[string]*containers.Container // postHookFn is the posthook function to call postHookFn func(context.Context) // timestamp of device event timestamp time.Time } type udevEvent struct{ *event } type containerEvent struct{ *event } type classEvent struct{ *event } type kubeletEvent struct{ *event } var ( requested = "requested" deviceExistsInClassFn = deviceExistsInClass ) // NewKubeletEvent returns an empty device event. This is to trigger // an update to the device plugins to ensure they are running and up-to-date. func NewKubeletEvent() DeviceEvent { return &kubeletEvent{ event: &event{ containers: map[string]*containers.Container{}, postHookFn: func(context.Context) {}, timestamp: time.Now(), }, } } func (e *event) Containers() map[string]*containers.Container { return e.containers } func (e *event) PosthookFunc() func(context.Context) { return e.postHookFn } func (e *event) Timestamp() time.Time { return e.timestamp } // ueventMatchesContainer checks to see if a uevent should be applied to a container given its list of device classes. // The function returns any matching device rules that apply to the container func ueventMatchesContainer(ctx context.Context, udevEvent *udev.UEvent, ctr *containers.Container, deviceClasses map[string]*dsv1.DeviceClass) bool { log := logger.FromContext(ctx) for classLabel := range ctr.Labels { if !class.IsDeviceClass(classLabel) { continue } deviceClass, ok := deviceClasses[classLabel] if !ok || deviceClass == nil { continue } devicePath := prefixSysPath(udevEvent.SysPath) existsInClass := deviceExistsInClassFn(deviceClass, devicePath) log.Log(ctx, logger.LevelTrace, "evaluating uevent matches device class", "class", classLabel, "container", ctr.Labels[cc.AnnContainerName], "event", udevEvent.SysPath, "matches class", existsInClass) if existsInClass { return true } } return false } func deviceExistsInClass(deviceClass *dsv1.DeviceClass, devicePath string) bool { return deviceClass.DeviceExistsInClass(devicePath) } func prefixSysPath(path string) string { if strings.HasPrefix(path, devices.SysPath) { return path } return filepath.Join(devices.SysPath, path) }