beyondstorage / go-storage Goto Github PK
View Code? Open in Web Editor NEWA vendor-neutral storage library for Golang: Write once, run on every storage service.
Home Page: https://beyondstorage.io
License: Apache License 2.0
A vendor-neutral storage library for Golang: Write once, run on every storage service.
Home Page: https://beyondstorage.io
License: Apache License 2.0
Proposal: https://github.com/Xuanwo/storage/blob/master/docs/design/11-error-handling.md
Spec: https://github.com/Xuanwo/storage/blob/master/docs/spec/1-error-handling.md
Allow user set storage class while init services.
Start from go 1.13, we will test target the latest two major version.
Any install/setup/build error report on not tested version will be marked as wontfix
This project intents to be a unified storage layer for Golang, but different storage layers' configuration are so different we can't unify them well.
For posixfs: we only need to specify the workdir.
For object storage: we need to specify host, port, protocol, access key id and others.
We used to support them by type and options, like we did in qscamel:
Every service(endpoint in qscamel) should handle their own options:
type Client struct {
BucketName string `yaml:"bucket_name"`
Endpoint string `yaml:"endpoint"`
Region string `yaml:"region"`
AccessKeyID string `yaml:"access_key_id"`
SecretAccessKey string `yaml:"secret_access_key"`
DisableSSL bool `yaml:"disable_ssl"`
UseAccelerate bool `yaml:"use_accelerate"`
PathStyle bool `yaml:"path_style"`
EnableListObjectsV2 bool `yaml:"enable_list_objects_v2"`
EnableSignatureV2 bool `yaml:"enable_signature_v2"`
DisableURICleaning bool `yaml:"disable_uri_cleaning"`
Path string
client *s3.S3
}
func New(ctx context.Context, et uint8, hc *http.Client) (c *Client, err error) {
...
content, err := yaml.Marshal(e.Options)
if err != nil {
return
}
err = yaml.Unmarshal(content, c)
if err != nil {
return
}
...
}
Developer who want to use this service should handle the type:
switch t.Src.Type {
...
case constants.EndpointS3:
src, err = s3.New(ctx, constants.SourceEndpoint, contexts.Client)
if err != nil {
return
}
...
default:
logrus.Errorf("Type %s is not supported.", t.Src.Type)
err = constants.ErrEndpointNotSupported
return
}
User should set them in config directly:
source:
type: s3
path: "/path/to/source"
options:
bucket_name: example_bucket
endpoint: example_endpoint
region: example_region
access_key_id: example_access_key_id
secret_access_key: example_secret_access_key
disable_ssl: false
use_accelerate: false
path_style: false
enable_list_objects_v2: false
enable_signature_v2: false
disable_uri_cleaning: false
It works, but it doesn't meet our goal. To address this problem, we split endpoint and credential in PR services: Split endpoint and credential into different pair. In this PR, we can init an object service like:
srv := qingstor.New()
err = srv.Init(
pairs.WithCredential(credential.NewStatic(accessKey, secretKey)),
pairs.WithEndpoint(endpoint.NewStaticFromParsedURL(protocol, host, port)),
)
if err != nil {
log.Printf("service init failed: %v", err)
}
It's better, but not enough. We need a general way to init all service like:
srv := storage.SomeCall("<type>", something)
So I propose following changes:
config string
is widely used in db connections:
mysql: user:password@/dbname?charset=utf8&parseTime=True&loc=Local
postgres: host=myhost port=myport user=gorm dbname=gorm password=mypassword
sqlserver: sqlserver://username:password@localhost:1433?database=dbname
Like we did in URL, we can use different part in a formatted string to represent different meaning.
Config string in storage would be like:
<type>://<config>
+
|
v
<credential>@<endpoint>/<name>?<options>
+ + +
| +------------+ +----------------------+
v v v
<protocol>:<data> <protocol>:<host>:<port> <key>:<value>[&<key>:<value>]
<protocol>:<data>
, static credential could be static://<access_key>:<secret_key>
.<protocol>:<host>:<port>
, qingstor's valid endpoint could be https://qingstor.com:443
, 80 and 443 can be emitted with matched protocol.<key>=<value>
connected with &
So a valid config string could be:
qos://static:<access_key_id>:<secret_access_key>@https:qingstor.com:443/<bucket_name>?zone=pek3b&work_dir=/storage
posixfs:///<path>
if opt.HasSize && opt.HasOffset {
return iowrap.SectionReadCloser(f, opt.Offset, opt.Size), nil
}
if opt.HasSize {
return iowrap.LimitReadCloser(f, opt.Size), nil
}
if opt.HasOffset {
_, err = f.Seek(opt.Offset, 0)
if err != nil {
return nil, fmt.Errorf(errorMessage, s, path, handleOsError(err))
}
}
if opt.HasReadCallbackFunc {
return iowrap.CallbackReadCloser(f, opt.ReadCallbackFunc), nil
}
should be like
if opt.HasSize {
r = iowrap.LimitReadCloser(f, opt.Size), nil
}
Expose Open(t string, opt []*types.Pair)
directly, and allow user to design their config format.
This proposal will close proposal: #167, and deprecate proposal 3-support-service-init-via-config-string
Let user to decide what we should do when we meet missing features:
Name
will be used in current Storager
which is relative to WorkDir
ID
is the ID in current services which is the AbsPath
or business IDDependabot couldn't parse the go.mod found at /go.mod
.
The error Dependabot encountered was:
go: cloud.google.com/go/[email protected] requires
golang.org/x/[email protected] requires
dmitri.shuralyov.com/gpu/[email protected]: unrecognized import path "dmitri.shuralyov.com/gpu/mtl" (https fetch: Get https://dmitri.shuralyov.com/gpu/mtl?go-get=1: EOF)
Port is not always required in pkg/endpoint
Parse.
https
: port could be default to 443
http
: port could be default to 80
strconv.NumError
as an exmaple: we need to write following code for unittest:
e := &strconv.NumError{}
ok := errors.As(err, &e)
if ok {
assert.True(t, errors.Is(e.Err, tt.err))
} else {
assert.True(t, errors.Is(err, tt.err))
}
After go 1.14 released, we can change them into:
assert.True(t, errors.Is(err, tt.err))
PR #53 adds a new metadata Id
.
This inspires me that we need an Id
across all services:
For local file system: Id
should be whole path towords root.
For object storage: Id
should be the whole key.
For dropbox alike SaaS: Id
should be their Id
in business.
So let's move Id
to object type instead of in metadata.
o := &types.Object{
ID: meta.Id,
Type: types.ObjectTypeFile,
Name: filepath.Join(path, meta.Name),
Size: int64(meta.Size),
UpdatedAt: meta.ServerModified,
ObjectMeta: metadata.NewObjectMeta(),
}
filepath.Join(path, meta.Name)
is not the valid name for this file, use path
directly?
Following proposals are sure to be included in v1.0.0 (a.k.a v0.9.0)
There are no ensurement for any new service to be included in v1.0.0
There will be two minor release before v1.0.0: v0.8.0 and v0.9.0
v0.8.0 will be released shortly after 2020-03-06, and v0.9.0 expected to be released in 2020-03-20.
After v0.9.0, no new proposals will be implemented until v1.0.0 released, and I will focus on bug fix and code cleanup.
Dependabot couldn't parse the go.mod found at /go.mod
.
The error Dependabot encountered was:
go: cloud.google.com/go/[email protected] requires
golang.org/x/[email protected] requires
dmitri.shuralyov.com/gpu/[email protected]: unrecognized import path "dmitri.shuralyov.com/gpu/mtl" (https fetch: Get https://dmitri.shuralyov.com/gpu/mtl?go-get=1: EOF)
segments map[string]*segment.Segment
segmentLock sync.RWMutex
can be a struct in package segment
.
Dependabot couldn't parse the go.mod found at /go.mod
.
The error Dependabot encountered was:
go: cloud.google.com/go/[email protected] requires
golang.org/x/[email protected] requires
dmitri.shuralyov.com/gpu/[email protected]: unrecognized import path "dmitri.shuralyov.com/gpu/mtl" (https fetch: Get https://dmitri.shuralyov.com/gpu/mtl?go-get=1: EOF)
As described in Proposal: Normalize Metadata, we need to normalize private meta's value.
The first one is storage-class
. Every object storage service has their own storage class set, we need to do convert between their SDK and storage
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.