package v1 import ( "context" "encoding/json" "errors" "fmt" "os" "path/filepath" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "edge-infra.dev/pkg/lib/filesystem" "edge-infra.dev/pkg/sds/devices/logger" ) const ( deviceClassCacheName = "deviceclasses.json" deviceCacheName = "devicesets.json" deviceRuleCacheName = "devicerules.json" deviceFilePerms os.FileMode = 0600 ) // readDeviceClassFromPersistence will read and convert the device classes // specified file path to a device class list func readDeviceClassFromPersistence(path string) (map[string]*DeviceClass, error) { path = filepath.Join(path, deviceClassCacheName) if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { return map[string]*DeviceClass{}, nil } jsonDoc, err := os.ReadFile(path) if err != nil { return map[string]*DeviceClass{}, err } deviceClasses := DeviceClassList{} if err := json.Unmarshal(jsonDoc, &deviceClasses); err != nil { return map[string]*DeviceClass{}, fmt.Errorf("failed to unmarshal to json: %v", err) } deviceClassMap := map[string]*DeviceClass{} for _, class := range deviceClasses.Items { newClass := &class deviceClassMap[class.ClassName()] = newClass } return deviceClassMap, nil } // readDeviceFromPersistence will read and convert the device classes // specified file path to a device class list func readDeviceFromPersistence(path string) ([]*DeviceSet, error) { path = filepath.Join(path, deviceCacheName) if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { return []*DeviceSet{}, nil } jsonDoc, err := os.ReadFile(path) if err != nil { return []*DeviceSet{}, err } deviceList := DeviceSetList{} if err := json.Unmarshal(jsonDoc, &deviceList); err != nil { return []*DeviceSet{}, fmt.Errorf("failed to unmarshal to json: %v", err) } devices := []*DeviceSet{} for _, device := range deviceList.Items { newDevice := &device devices = append(devices, newDevice) } return devices, nil } // Write will write list of device classes to file system in json format func writeDeviceClassCache(ctx context.Context, path string, deviceClasses []DeviceClass) error { log := logger.FromContext(ctx) classNames := []string{} path = filepath.Join(path, deviceClassCacheName) // remove annotations, labels and metadata for idx, class := range deviceClasses { metadata := metav1.ObjectMeta{ Name: class.ObjectMeta.GetName(), Generation: class.ObjectMeta.Generation, } class.ObjectMeta = metadata deviceClasses[idx] = class classNames = append(classNames, class.ClassName()) } log.Debug("caching device classes", "classes", classNames) target, err := json.Marshal(DeviceClassList{Items: deviceClasses}) if err != nil { return fmt.Errorf("failed to marshal to json: %v", err) } return applyWriteIfDiff(target, path) } // Write will write list of device classes to file system in json format func writeDevicesCache(ctx context.Context, path string, devices []DeviceSet) error { log := logger.FromContext(ctx) names := []string{} path = filepath.Join(path, deviceCacheName) // remove annotations, labels and metadata for idx, dev := range devices { metadata := metav1.ObjectMeta{ Name: dev.ObjectMeta.GetName(), Generation: dev.ObjectMeta.Generation, } dev.ObjectMeta = metadata devices[idx] = dev names = append(names, dev.Name()) } log.Debug("caching devices", "devices", names) target, err := json.Marshal(DeviceSetList{Items: devices}) if err != nil { return fmt.Errorf("failed to marshal to json: %v", err) } return applyWriteIfDiff(target, path) } // applyWriteIfDiff will only write target contents if there is // a diff between the current and target content func applyWriteIfDiff(target []byte, path string) error { isEqual, err := filesystem.FileEqualsTarget(path, target) if errors.Is(err, os.ErrNotExist) || isEqual { return os.WriteFile(path, target, deviceFilePerms) } return nil }