package storage import ( "context" "errors" "fmt" "io" "os" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" ) var ErrRangeNotSupported = errors.New("byte range not suppoered") type minioStorage struct { name string client *minio.Client bucketName string prefix string } func createMinioBackend(name string, params map[string]string) (Backend, error) { if params["endpoint"] == "" { return nil, errors.New("endpoint not provided") } if params["key_id"] == "" { return nil, errors.New("key id not provided") } if params["access_key"] == "" { return nil, errors.New("access key not provided") } if params["bucket_name"] == "" { return nil, errors.New("bucket name not provided") } client, err := minio.New(params["endpoint"], &minio.Options{ Creds: credentials.NewStaticV4(params["key_id"], params["access_key"], ""), Secure: true, }) if err != nil { return nil, err } return minioStorage{name: name, client: client, bucketName: params["bucket_name"], prefix: params["prefix"]}, nil } func (s minioStorage) Name() string { return s.name } func (s minioStorage) OpenRead(name string, start, length int) (io.ReadCloser, error) { // TODO: Range if r, err := s.client.GetObject(context.Background(), s.bucketName, s.prefix+name, minio.GetObjectOptions{}); err != nil { return nil, err } else { return r, ErrRangeNotSupported } } func (s minioStorage) Copy(ctx context.Context, src Backend, srcName, destName string) error { if src == s && srcName == destName { return errors.New("cannot copy to self") } var localPath string if l, ok := src.(LocalBackend); ok { localPath = l.path(srcName) } else { f, err := os.CreateTemp(tempDir, "phylum-*") if err != nil { return err } defer func() { os.Remove(f.Name()) }() in, err := src.OpenRead(srcName, 0, -1) if err != nil { return err } defer in.Close() _, copyErr := io.Copy(f, in) closeErr := f.Close() if copyErr != nil { return copyErr } if closeErr != nil { return closeErr } localPath = f.Name() } _, err := s.client.FPutObject( ctx, s.bucketName, s.prefix+destName, localPath, minio.PutObjectOptions{}, ) return err } func (s minioStorage) Delete(name string) error { return s.client.RemoveObject(context.Background(), s.bucketName, s.prefix+name, minio.RemoveObjectOptions{}) } func (s minioStorage) DeleteAll(names []string) []error { objs := make(chan minio.ObjectInfo) go func() { defer close(objs) for _, name := range names { objs <- minio.ObjectInfo{ Key: s.prefix + name, } } }() errChan := s.client.RemoveObjects(context.Background(), s.bucketName, objs, minio.RemoveObjectsOptions{}) errs := make([]error, 0) for err := range errChan { errs = append(errs, err.Err) } return errs } func (s minioStorage) String() string { return fmt.Sprintf("minio (endpoint: %s, bucket: %s, prefix: %s)", s.client.EndpointURL(), s.bucketName, s.prefix) } // type progressReader struct { // cur, len int // callback func() // } // func (r *progressReader) Read(p []byte) (n int, err error) { // max := r.len - r.cur // size := len(p) // if size > max { // size = max // } // r.cur += size // if r.cur == r.len && size == 0 { // r.callback() // } // return size, nil // }