e36bb0bd创建于 2025年4月11日历史提交
/*
 * This file is part of the KubeVirt project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright The KubeVirt Authors.
 *
 */

package cache

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"

	dutils "kubevirt.io/kubevirt/pkg/ephemeral-disk-utils"
	kfs "kubevirt.io/kubevirt/pkg/os/fs"
)

type Cache struct {
	path string
	fs   cacheFS
}

type cacheFS interface {
	Stat(name string) (os.FileInfo, error)
	MkdirAll(path string, perm os.FileMode) error
	RemoveAll(path string) error
	ReadFile(filename string) ([]byte, error)
	WriteFile(filename string, data []byte, perm fs.FileMode) error
}

type CacheCreator struct{}

func (_ CacheCreator) New(filePath string) *Cache {
	return NewCustomCache(filePath, kfs.New())
}

func NewCustomCache(path string, fs cacheFS) *Cache {
	return &Cache{path, fs}
}

func (c Cache) Entry(path string) (Cache, error) {
	fileInfo, err := c.fs.Stat(c.path)
	if err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			return Cache{}, fmt.Errorf("unable to define entry: %v", err)
		}
	} else if !fileInfo.IsDir() {
		return Cache{}, fmt.Errorf("unable to define entry: parent cache has an existing store")
	}

	return Cache{
		path: filepath.Join(c.path, path),
		fs:   c.fs,
	}, nil
}

func (c Cache) Read(data interface{}) (interface{}, error) {
	err := readFromCachedFile(c.fs, data, c.path)
	return data, err
}

func (c Cache) Write(data interface{}) error {
	return writeToCachedFile(c.fs, data, c.path)
}

func (c Cache) Delete() error {
	return c.fs.RemoveAll(c.path)
}

type cacheCreator interface {
	New(filePath string) *Cache
}

func writeToCachedFile(fs cacheFS, obj interface{}, fileName string) error {
	if err := fs.MkdirAll(filepath.Dir(fileName), 0750); err != nil {
		return err
	}
	buf, err := json.MarshalIndent(&obj, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshaling cached object: %v", err)
	}

	err = fs.WriteFile(fileName, buf, 0604)
	if err != nil {
		return fmt.Errorf("error writing cached object: %v", err)
	}
	return dutils.DefaultOwnershipManager.UnsafeSetFileOwnership(fileName)
}

func readFromCachedFile(fs cacheFS, obj interface{}, fileName string) error {
	buf, err := fs.ReadFile(fileName)
	if err != nil {
		return err
	}

	err = json.Unmarshal(buf, &obj)
	if err != nil {
		return fmt.Errorf("error unmarshaling cached object: %v", err)
	}
	return nil
}