2025-04-01 10:38:02 +09:00

100 lines
2.1 KiB
Go

package dualwrite
import (
"context"
"encoding/json"
"os"
"sync"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/grafana/grafana-app-sdk/logging"
)
// Simple file implementation -- useful while testing and not yet sure about the SQL structure!
// When a path exists, read/write it from disk; otherwise it is held in memory
type fileDB struct {
path string
changed int64
db map[string]StorageStatus
mu sync.RWMutex
logger logging.Logger
}
// File implementation while testing -- values are saved in the data directory
func newFileDB(path string) *fileDB {
return &fileDB{
db: make(map[string]StorageStatus),
path: path,
logger: logging.DefaultLogger.With("logger", "fileDB"),
}
}
func (m *fileDB) Get(ctx context.Context, gr schema.GroupResource) (StorageStatus, bool, error) {
m.mu.RLock()
defer m.mu.RUnlock()
info, err := os.Stat(m.path)
if err == nil && info.ModTime().UnixMilli() != m.changed {
v, err := os.ReadFile(m.path)
if err == nil {
err = json.Unmarshal(v, &m.db)
m.changed = info.ModTime().UnixMilli()
}
if err != nil {
m.logger.Warn("error reading filedb", "err", err)
}
changed := false
for k, v := range m.db {
// Must write to unified if we are reading unified
if v.ReadUnified && !v.WriteUnified {
v.WriteUnified = true
m.db[k] = v
changed = true
}
// Make sure we are writing something!
if !(v.WriteLegacy || v.WriteUnified) {
v.WriteLegacy = true
m.db[k] = v
changed = true
}
}
if changed {
err = m.save()
m.logger.Warn("error saving changes filedb", "err", err)
}
}
v, ok := m.db[gr.String()]
return v, ok, nil
}
func (m *fileDB) Set(ctx context.Context, status StorageStatus) error {
m.mu.Lock()
defer m.mu.Unlock()
gr := schema.GroupResource{
Group: status.Group,
Resource: status.Resource,
}
m.db[gr.String()] = status
return m.save()
}
func (m *fileDB) save() error {
if m.path != "" {
data, err := json.MarshalIndent(m.db, "", " ")
if err != nil {
return err
}
err = os.WriteFile(m.path, data, 0644)
if err != nil {
return err
}
}
return nil
}