Files
filestore/remote/miniostorage/miniostorage.go
T

149 lines
3.7 KiB
Go

package miniostorage
import (
"context"
"errors"
"net/url"
"os"
"strconv"
"strings"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"git.company.lan/gopkg/filestore/remote"
)
const defaultFileMode = 0o755
var _ remote.Storage = (*MinioStorage)(nil)
func init() {
remote.Register("minio", &MinioStorage{})
}
type MinioStorage struct {
ctx context.Context
client *minio.Client
bucket string
separator string
}
// NewStorage
func (s *MinioStorage) NewStorage(ctx context.Context, connString string) (remote.Storage, error) {
u, err := url.Parse(connString)
if err != nil {
return nil, err
}
queries := u.Query()
username, password := getUserPassword(u)
token := queries.Get("token")
opts := &minio.Options{
Creds: credentials.NewStaticV4(username, password, token),
Region: "us-east-1",
}
if queries.Has("secure") {
secure, err := strconv.ParseBool(queries.Get("secure"))
if err != nil {
return nil, err
}
opts.Secure = secure
}
if queries.Has("region") {
opts.Region = queries.Get("region")
}
client, err := minio.New(u.Host, opts)
if err != nil {
return nil, err
}
s.ctx = ctx
s.client = client
s.bucket = u.Path[1:]
s.separator = "/"
return s, nil
}
// normSeparators will normalize all "\\" and "/" to the provided separator
func (s *MinioStorage) normSeparators(str string) string {
return strings.Replace(strings.Replace(str, "\\", s.separator, -1), "/", s.separator, -1)
}
// Create
func (s *MinioStorage) Create(name string) (remote.File, error) {
return s.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0)
}
// Open
func (s *MinioStorage) Open(name string) (remote.File, error) {
return s.OpenFile(name, os.O_RDONLY, 0)
}
// OpenFile
func (s *MinioStorage) OpenFile(name string, flag int, fileMode os.FileMode) (remote.File, error) {
if flag&os.O_APPEND != 0 {
return nil, errors.New("appending files will lead to trouble")
}
name = strings.TrimPrefix(s.normSeparators(name), s.separator)
file := NewMinioFile(s.ctx, s, flag, fileMode, name)
var err error
if flag&os.O_CREATE != 0 {
_, err = file.WriteString("")
}
return file, err
}
// Remove
func (s *MinioStorage) Remove(name string) error {
name = strings.TrimPrefix(s.normSeparators(name), s.separator)
return s.client.RemoveObject(s.ctx, s.bucket, name, minio.RemoveObjectOptions{GovernanceBypass: true})
}
// RemoveAll
func (s *MinioStorage) RemoveAll(path string) error {
path = strings.TrimPrefix(s.normSeparators(path), s.separator)
objectsCh := make(chan minio.ObjectInfo)
go func() {
defer close(objectsCh)
opts := minio.ListObjectsOptions{Prefix: path, Recursive: true}
for object := range s.client.ListObjects(s.ctx, s.bucket, opts) {
if object.Err != nil {
panic(object.Err)
}
objectsCh <- object
}
}()
errorCh := s.client.RemoveObjects(s.ctx, s.bucket, objectsCh, minio.RemoveObjectsOptions{})
for e := range errorCh {
return errors.New("Failed to remove " + e.ObjectName + ", error: " + e.Err.Error())
}
return nil
}
// Rename
func (s *MinioStorage) Rename(oldName, newName string) error {
if oldName == newName {
return nil
}
oldName = strings.TrimPrefix(s.normSeparators(oldName), s.separator)
newName = strings.TrimPrefix(s.normSeparators(newName), s.separator)
src := minio.CopySrcOptions{
Bucket: s.bucket,
Object: oldName,
}
dst := minio.CopyDestOptions{
Bucket: s.bucket,
Object: newName,
}
if _, err := s.client.CopyObject(s.ctx, dst, src); err != nil {
return err
}
return s.Remove(oldName)
}
// Stat
func (s *MinioStorage) Stat(name string) (os.FileInfo, error) {
name = strings.TrimPrefix(s.normSeparators(name), s.separator)
file := NewMinioFile(s.ctx, s, os.O_RDWR, defaultFileMode, name)
return file.Stat()
}