Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
3.7 kB · 159 lines
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160package artifactstore
import ( "context" "errors" "fmt" "io" "os" "path/filepath"
"github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" spindleconfig "tangled.org/core/spindle/config" "tangled.org/core/spindle/storage")
type Writer interface { Put(ctx context.Context, ref string, r io.Reader) error}
type Reader interface { Open(ctx context.Context, ref string) (io.ReadCloser, error)}
// missing refs are not errorstype Store interface { Writer Reader Delete(ctx context.Context, ref string) error}
type storageStore struct { storage.Storage}
func (s storageStore) Open(ctx context.Context, ref string) (io.ReadCloser, error) { return s.Get(ctx, ref)}
type diskStore struct { storageStore root string}
func newDiskStore(root string) (*diskStore, error) { backend, err := storage.NewDisk(root) if err != nil { return nil, err } return &diskStore{ storageStore: storageStore{Storage: backend}, root: filepath.Clean(root), }, nil}
func (s *diskStore) target(ref string) (string, error) { if err := storage.ValidateKey(ref); err != nil { return "", err } return filepath.Join(s.root, filepath.FromSlash(ref)), nil}
type Stores struct { order []string stores map[string]Store}
func NewStores(cfg spindleconfig.ArtifactStores, diskFallback, legacyS3Bucket string) (*Stores, error) { stores := &Stores{stores: make(map[string]Store)} diskDir := cfg.Disk.Dir if diskDir == "" { diskDir = diskFallback } if diskDir != "" { disk, err := newDiskStore(diskDir) if err != nil { return nil, err } stores.order = append(stores.order, "disk") stores.stores["disk"] = disk }
bucket := cfg.S3.Bucket if bucket == "" { bucket = legacyS3Bucket } if bucket != "" { awsCfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(cfg.S3.Region)) if err != nil { return nil, fmt.Errorf("load aws config: %w", err) } backend, err := storage.NewS3(s3.NewFromConfig(awsCfg), bucket, "") if err != nil { return nil, err } stores.order = append(stores.order, "s3") stores.stores["s3"] = storageStore{Storage: backend} } return stores, nil}
func (s *Stores) Names() []string { return append([]string(nil), s.order...)}
func (s *Stores) Store(name string) (Store, bool) { store, ok := s.stores[name] return store, ok}
func (s *Stores) Open(ctx context.Context, ref string) (io.ReadCloser, error) { var errs []error for _, name := range s.order { rc, err := s.stores[name].Open(ctx, ref) if err == nil { return rc, nil } errs = append(errs, fmt.Errorf("%s: %w", name, err)) } return nil, fmt.Errorf("open artifact %q: %w", ref, errors.Join(errs...))}
func (s *Stores) PutFile(ctx context.Context, ref, sourcePath string) []error { var errs []error for _, name := range s.order { store := s.stores[name] if disk, ok := store.(*diskStore); ok { target, err := disk.target(ref) if err == nil { source, sourceErr := filepath.Abs(sourcePath) targetAbs, targetErr := filepath.Abs(target) if sourceErr == nil && targetErr == nil && source == targetAbs { continue } } } file, err := os.Open(sourcePath) if err != nil { errs = append(errs, fmt.Errorf("%s: open source: %w", name, err)) continue } err = store.Put(ctx, ref, file) _ = file.Close() if err != nil { errs = append(errs, fmt.Errorf("%s: %w", name, err)) } } return errs}
func (s *Stores) Delete(ctx context.Context, ref string) []error { var errs []error for _, name := range s.order { if err := s.stores[name].Delete(ctx, ref); err != nil { errs = append(errs, fmt.Errorf("%s: %w", name, err)) } } return errs}