f14fabc90a
This change adds a new forget-satellite sub-command to the storagenode CLI which cleans up untrusted satellite data. Issue: https://github.com/storj/storj/issues/6068 Change-Id: Iafa109fdc98afdba7582f568a61c22222da65f02
131 lines
3.3 KiB
Go
131 lines
3.3 KiB
Go
// Copyright (C) 2019 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package trust
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
"storj.io/common/fpath"
|
|
"storj.io/common/storj"
|
|
)
|
|
|
|
// Cache caches source information about trusted satellites.
|
|
type Cache struct {
|
|
path string
|
|
data *CacheData
|
|
}
|
|
|
|
// LoadCache loads a cache from a file on disk. If the file is not present, the
|
|
// cache is still loaded. If the file cannot be read for any other reason, the
|
|
// function will return an error. LoadCache ensures the containing directory
|
|
// exists.
|
|
func LoadCache(path string) (*Cache, error) {
|
|
if path == "" {
|
|
return nil, Error.New("cache path cannot be empty")
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
|
|
return nil, Error.New("unable to make cache parent directory: %w", err)
|
|
}
|
|
|
|
data, err := LoadCacheData(path)
|
|
switch {
|
|
case err == nil:
|
|
case errs.IsFunc(err, os.IsNotExist):
|
|
data = NewCacheData()
|
|
default:
|
|
return nil, err
|
|
}
|
|
|
|
return &Cache{
|
|
path: path,
|
|
data: data,
|
|
}, nil
|
|
}
|
|
|
|
// Path returns the path on disk to the file containing the cache.
|
|
func (cache *Cache) Path() string {
|
|
return cache.path
|
|
}
|
|
|
|
// Lookup takes a cache key and returns entries associated with that key. If
|
|
// the key is unset in the cache, false is returned for ok. Otherwise the
|
|
// entries are returned with ok returned as true.
|
|
func (cache *Cache) Lookup(key string) (entries []Entry, ok bool) {
|
|
entries, ok = cache.data.Entries[key]
|
|
return entries, ok
|
|
}
|
|
|
|
// Set sets the entries in the cache for the provided key.
|
|
func (cache *Cache) Set(key string, entries []Entry) {
|
|
cache.data.Entries[key] = entries
|
|
}
|
|
|
|
// DeleteSatelliteEntry searches the cache for the provided satellite ID and removes it if
|
|
// found.
|
|
func (cache *Cache) DeleteSatelliteEntry(satelliteID storj.NodeID) (deleted bool) {
|
|
for s, entries := range cache.data.Entries {
|
|
for i, entry := range entries {
|
|
if entry.SatelliteURL.ID == satelliteID {
|
|
cache.data.Entries[s] = append(entries[:i], entries[i+1:]...)
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Save persists the cache to disk.
|
|
func (cache *Cache) Save(ctx context.Context) (err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
return SaveCacheData(cache.path, cache.data)
|
|
}
|
|
|
|
// CacheData represents the data stored in the cache.
|
|
type CacheData struct {
|
|
Entries map[string][]Entry `json:"entries"`
|
|
}
|
|
|
|
// NewCacheData returns an new CacheData.
|
|
func NewCacheData() *CacheData {
|
|
return &CacheData{
|
|
Entries: make(map[string][]Entry),
|
|
}
|
|
}
|
|
|
|
// LoadCacheData loads the cache data from the given path.
|
|
func LoadCacheData(path string) (*CacheData, error) {
|
|
dataBytes, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, Error.Wrap(err)
|
|
}
|
|
|
|
data := NewCacheData()
|
|
if err := json.Unmarshal(dataBytes, data); err != nil {
|
|
return nil, Error.New("malformed cache: %w", err)
|
|
}
|
|
// Ensure the entries map is always non-nil on load
|
|
if data.Entries == nil {
|
|
data.Entries = map[string][]Entry{}
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// SaveCacheData persists the cache data to the given path.
|
|
func SaveCacheData(path string, data *CacheData) error {
|
|
// Ensure the entries map is always non-nil on save
|
|
if data.Entries == nil {
|
|
data.Entries = map[string][]Entry{}
|
|
}
|
|
dataBytes, err := json.MarshalIndent(data, "", " ")
|
|
if err != nil {
|
|
return Error.Wrap(err)
|
|
}
|
|
return fpath.AtomicWriteFile(path, dataBytes, 0644)
|
|
}
|