grafana/pkg/infra/filestorage/filestorage.go
Artur Wierzbicki a8b90d9a25
FileStore: add basic file storage API (#46051)
* #45498: fs API alpha

* #45498: remove grafanaDS changes for filestorage.go

* #45498: fix lint

* #45498: fix lint

* #45498: remove db file storage migration

* #45498: linting

* #45498: linting

* #45498: linting

* #45498: fix imports

* #45498: add comment

* remove StorageName abstractions

* FileStore: add dummy implementation (#46071)

* #45498: bring back grafanaDs changes, add dummy filestorage

* #45498: rename grafanaDs to public

* #45498: modify join

* #45498: review fix

* #45498:  unnecessary leading newline (whitespace) IMPORTANT FIX

* #45498: fix belongsToStorage

* #45498: fix removeStoragePrefix so that it works with abs paths

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-03-03 10:53:26 +04:00

161 lines
3.8 KiB
Go

package filestorage
import (
"context"
"errors"
"fmt"
"os"
"strings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"gocloud.dev/blob"
_ "gocloud.dev/blob/fileblob"
_ "gocloud.dev/blob/memblob"
)
const (
ServiceName = "FileStorage"
)
func ProvideService(features featuremgmt.FeatureToggles, cfg *setting.Cfg) (FileStorage, error) {
grafanaDsStorageLogger := log.New("grafanaDsStorage")
path := fmt.Sprintf("file://%s", cfg.StaticRootPath)
grafanaDsStorageLogger.Info("Initializing grafana ds storage", "path", path)
bucket, err := blob.OpenBucket(context.Background(), path)
if err != nil {
currentDir, _ := os.Getwd()
grafanaDsStorageLogger.Error("Failed to initialize grafana ds storage", "path", path, "error", err, "cwd", currentDir)
return nil, err
}
prefixes := []string{
"testdata/",
"img/icons/",
"img/bg/",
"gazetteer/",
"maps/",
"upload/",
}
var grafanaDsStorage FileStorage
if features.IsEnabled(featuremgmt.FlagFileStoreApi) {
grafanaDsStorage = &wrapper{
log: grafanaDsStorageLogger,
wrapped: cdkBlobStorage{
log: grafanaDsStorageLogger,
bucket: bucket,
rootFolder: "",
},
pathFilters: &PathFilters{allowedPrefixes: prefixes},
}
} else {
grafanaDsStorage = &dummyFileStorage{}
}
return &service{
grafanaDsStorage: grafanaDsStorage,
log: log.New("fileStorageService"),
}, nil
}
type service struct {
log log.Logger
grafanaDsStorage FileStorage
}
func (b service) Get(ctx context.Context, path string) (*File, error) {
var filestorage FileStorage
if belongsToStorage(path, StorageNamePublic) {
filestorage = b.grafanaDsStorage
path = removeStoragePrefix(path)
}
if err := validatePath(path); err != nil {
return nil, err
}
return filestorage.Get(ctx, path)
}
func removeStoragePrefix(path string) string {
path = strings.TrimPrefix(path, Delimiter)
if path == Delimiter || path == "" {
return Delimiter
}
if !strings.Contains(path, Delimiter) {
return Delimiter
}
split := strings.Split(path, Delimiter)
// root of storage
if len(split) == 2 && split[1] == "" {
return Delimiter
}
// replace storage
split[0] = ""
return strings.Join(split, Delimiter)
}
func (b service) Delete(ctx context.Context, path string) error {
return errors.New("not implemented")
}
func (b service) Upsert(ctx context.Context, file *UpsertFileCommand) error {
return errors.New("not implemented")
}
func (b service) ListFiles(ctx context.Context, path string, cursor *Paging, options *ListOptions) (*ListFilesResponse, error) {
var filestorage FileStorage
if belongsToStorage(path, StorageNamePublic) {
filestorage = b.grafanaDsStorage
path = removeStoragePrefix(path)
} else {
return nil, errors.New("not implemented")
}
if err := validatePath(path); err != nil {
return nil, err
}
return filestorage.ListFiles(ctx, path, cursor, options)
}
func (b service) ListFolders(ctx context.Context, path string, options *ListOptions) ([]FileMetadata, error) {
var filestorage FileStorage
if belongsToStorage(path, StorageNamePublic) {
filestorage = b.grafanaDsStorage
path = removeStoragePrefix(path)
} else {
return nil, errors.New("not implemented")
}
if err := validatePath(path); err != nil {
return nil, err
}
return filestorage.ListFolders(ctx, path, options)
}
func (b service) CreateFolder(ctx context.Context, path string) error {
return errors.New("not implemented")
}
func (b service) DeleteFolder(ctx context.Context, path string) error {
return errors.New("not implemented")
}
func (b service) IsFolderEmpty(ctx context.Context, path string) (bool, error) {
return true, errors.New("not implemented")
}
func (b service) close() error {
return b.grafanaDsStorage.close()
}