From 6d5211e17221f1643ab935f672c23457d10e9232 Mon Sep 17 00:00:00 2001 From: Todd Treece <360020+toddtreece@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:21:57 -0500 Subject: [PATCH] K8s: Fix windows filepath issue in file storage (#81919) Co-authored-by: Dan Cech --- pkg/services/apiserver/storage/file/file.go | 49 ++++++++++--------- .../apiserver/storage/file/restoptions.go | 16 +++--- pkg/services/apiserver/storage/file/util.go | 13 ++++- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/pkg/services/apiserver/storage/file/file.go b/pkg/services/apiserver/storage/file/file.go index 21b0a326f70..12d376b448c 100644 --- a/pkg/services/apiserver/storage/file/file.go +++ b/pkg/services/apiserver/storage/file/file.go @@ -37,15 +37,16 @@ var errResourceVersionSetOnCreate = errors.New("resourceVersion should not be se // Storage implements storage.Interface and storage resources as JSON files on disk. type Storage struct { - root string - gr schema.GroupResource - codec runtime.Codec - keyFunc func(obj runtime.Object) (string, error) - newFunc func() runtime.Object - newListFunc func() runtime.Object - getAttrsFunc storage.AttrFunc - trigger storage.IndexerFuncs - indexers *cache.Indexers + root string + resourcePrefix string + gr schema.GroupResource + codec runtime.Codec + keyFunc func(obj runtime.Object) (string, error) + newFunc func() runtime.Object + newListFunc func() runtime.Object + getAttrsFunc storage.AttrFunc + trigger storage.IndexerFuncs + indexers *cache.Indexers watchSet *WatchSet } @@ -78,20 +79,22 @@ func NewStorage( trigger storage.IndexerFuncs, indexers *cache.Indexers, ) (storage.Interface, factory.DestroyFunc, error) { - if err := ensureDir(resourcePrefix); err != nil { - return nil, func() {}, fmt.Errorf("could not establish a writable directory at path=%s", resourcePrefix) + root := config.Prefix + if err := ensureDir(root); err != nil { + return nil, func() {}, fmt.Errorf("could not establish a writable directory at path=%s", root) } ws := NewWatchSet() return &Storage{ - root: resourcePrefix, - gr: config.GroupResource, - codec: config.Codec, - keyFunc: keyFunc, - newFunc: newFunc, - newListFunc: newListFunc, - getAttrsFunc: getAttrsFunc, - trigger: trigger, - indexers: indexers, + root: root, + resourcePrefix: resourcePrefix, + gr: config.GroupResource, + codec: config.Codec, + keyFunc: keyFunc, + newFunc: newFunc, + newListFunc: newListFunc, + getAttrsFunc: getAttrsFunc, + trigger: trigger, + indexers: indexers, watchSet: ws, }, func() { @@ -352,7 +355,9 @@ func (s *Storage) GetList(ctx context.Context, key string, opts storage.ListOpti } } - objs, err := readDirRecursive(s.codec, key, s.newFunc) + dirname := s.dirPath(key) + + objs, err := readDirRecursive(s.codec, dirname, s.newFunc) if err != nil { return err } @@ -524,5 +529,5 @@ func (s *Storage) validateMinimumResourceVersion(minimumResourceVersion string, } func (s *Storage) nameFromKey(key string) string { - return strings.Replace(key, s.root+"/", "", 1) + return strings.Replace(key, s.resourcePrefix+"/", "", 1) } diff --git a/pkg/services/apiserver/storage/file/restoptions.go b/pkg/services/apiserver/storage/file/restoptions.go index 1b70af47bd6..34ac132fa73 100644 --- a/pkg/services/apiserver/storage/file/restoptions.go +++ b/pkg/services/apiserver/storage/file/restoptions.go @@ -3,7 +3,8 @@ package file import ( - "path" + "os" + "path/filepath" "time" "k8s.io/apimachinery/pkg/runtime/schema" @@ -21,7 +22,7 @@ type RESTOptionsGetter struct { func NewRESTOptionsGetter(path string, originalStorageConfig storagebackend.Config) *RESTOptionsGetter { if path == "" { - path = "/tmp/grafana-apiserver" + path = filepath.Join(os.TempDir(), "grafana-apiserver") } return &RESTOptionsGetter{path: path, original: originalStorageConfig} @@ -47,11 +48,12 @@ func (r *RESTOptionsGetter) GetRESTOptions(resource schema.GroupResource) (gener } ret := generic.RESTOptions{ - StorageConfig: storageConfig, - Decorator: NewStorage, - DeleteCollectionWorkers: 0, - EnableGarbageCollection: false, - ResourcePrefix: path.Join(storageConfig.Prefix, resource.Group, resource.Resource), + StorageConfig: storageConfig, + Decorator: NewStorage, + DeleteCollectionWorkers: 0, + EnableGarbageCollection: false, + // k8s expects forward slashes here, we'll convert them to os path separators in the storage + ResourcePrefix: "/" + resource.Group + "/" + resource.Resource, CountMetricPollPeriod: 1 * time.Second, StorageObjectCountTracker: storageConfig.Config.StorageObjectCountTracker, } diff --git a/pkg/services/apiserver/storage/file/util.go b/pkg/services/apiserver/storage/file/util.go index f7cbdf08b53..26885dde8c7 100644 --- a/pkg/services/apiserver/storage/file/util.go +++ b/pkg/services/apiserver/storage/file/util.go @@ -10,12 +10,23 @@ import ( "errors" "os" "path/filepath" + "strings" "k8s.io/apimachinery/pkg/runtime" ) func (s *Storage) filePath(key string) string { - return key + ".json" + // Replace backslashes with underscores to avoid creating bogus subdirectories + key = strings.Replace(key, "\\", "_", -1) + fileName := filepath.Join(s.root, filepath.Clean(key+".json")) + return fileName +} + +func (s *Storage) dirPath(key string) string { + // Replace backslashes with underscores to avoid creating bogus subdirectories + key = strings.Replace(key, "\\", "_", -1) + dirName := filepath.Join(s.root, filepath.Clean(key)) + return dirName } func writeFile(codec runtime.Codec, path string, obj runtime.Object) error {