From b68b74287f7bc210ed8fd355ae79893ec40a14d9 Mon Sep 17 00:00:00 2001 From: Brian Maher Date: Fri, 2 Aug 2019 06:58:32 -0700 Subject: [PATCH] New: storage API. --- storage/api.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++ storage/factory.go | 49 ++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 storage/api.go create mode 100644 storage/factory.go diff --git a/storage/api.go b/storage/api.go new file mode 100644 index 0000000..988899e --- /dev/null +++ b/storage/api.go @@ -0,0 +1,79 @@ +package storage + +// Look here for various implementations: +// https://github.com/puppetlabs/nebula-libs/tree/master/storage" +// + +import ( + "context" + "io" + "fmt" +) + +type ErrorCode string + +const ( + AuthError ErrorCode = "AuthError" + NotFoundError ErrorCode = "NotFoundError" + TimeoutError ErrorCode = "TimeoutError" + UnknownError ErrorCode = "UnknownError" +) + +type errorImpl struct { + message string + code ErrorCode + cause error +} + +func (e *errorImpl) Error() string { + return e.message +} + +func (e *errorImpl) Unwrap() error { + return e.cause +} + +func Errorf(cause error, code ErrorCode, format string, a ...interface{}) error { + return &errorImpl{ + code: code, + message: fmt.Sprintf(format, a...), + cause: cause, + } +} + +func IsAuthError(err error) bool { + e, ok := err.(*errorImpl) + return ok && e.code == AuthError +} + +func IsNotFoundError(err error) bool { + e, ok := err.(*errorImpl) + return ok && e.code == NotFoundError +} + +func IsTimeoutError(err error) bool { + e, ok := err.(*errorImpl) + return ok && e.code == TimeoutError +} + +type Sink func(io.Writer) error +type Source func(*Meta, io.Reader) error + +type Meta struct { + ContentType string +} +type PutOptions struct { + ContentType string +} +type GetOptions struct { + // TODO: Support range requests? +} +type DeleteOptions struct { + // TODO: Support conditional deletes? +} + +type BlobStore interface { + Put(ctx context.Context, key string, sink Sink, opts PutOptions) error + Get(ctx context.Context, key string, source Source, opts GetOptions) error + Delete(ctx context.Context, key string, opts DeleteOptions) error +} diff --git a/storage/factory.go b/storage/factory.go new file mode 100644 index 0000000..008ca87 --- /dev/null +++ b/storage/factory.go @@ -0,0 +1,49 @@ +package storage + +import ( + "fmt" + "net/url" + "strings" + "sync" +) + +var ( + factoriesMu sync.RWMutex + factories = make(map[string]BlobStoreFactory) +) + +type BlobStoreFactory func(url.URL) (BlobStore, error) + +func NewBlobStore(u url.URL) (BlobStore, error) { + scheme := strings.ToLower(u.Scheme) + factoriesMu.RLock() + factory, ok := factories[scheme] + factoriesMu.RUnlock() + if !ok { + return nil, fmt.Errorf("stroage: unknown scheme %q (forgotten import?)", scheme) + } + return factory(u) +} + +func RegisterFactory(scheme string, factory BlobStoreFactory) { + scheme = strings.ToLower(scheme) + factoriesMu.Lock() + defer factoriesMu.Unlock() + if nil == factory { + panic("storage: RegisterFactory passed a nil factory") + } + if _, dup := factories[scheme]; dup { + panic("storage: RegisterFactory called twice for factory " + scheme) + } + factories[scheme] = factory +} + +func SupportedSchemes() []string { + factoriesMu.RLock() + defer factoriesMu.RUnlock() + var list []string + for scheme := range factories { + list = append(list, scheme) + } + return list +}