156 lines
3.0 KiB
Go
156 lines
3.0 KiB
Go
// Copyright 2020 The Tango Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"code.gitea.io/log"
|
|
minio "github.com/minio/minio-go/v6"
|
|
)
|
|
|
|
var (
|
|
endpoint = flag.String("endpoint", "", "s3 endpoint")
|
|
accessKeyID = flag.String("accessKeyID", "", "s3 accessKeyID")
|
|
secretAccessKey = flag.String("secretAccessKey", "", "s3 secret access key")
|
|
bucket = flag.String("bucket", "", "s3 bucket")
|
|
useSSL = flag.Bool("useSSL", false, "if use SSL")
|
|
)
|
|
|
|
func buildMinioPath(p string) string {
|
|
return strings.TrimPrefix(p, "/")
|
|
}
|
|
|
|
func buildMinioDir(p string) string {
|
|
v := buildMinioPath(p)
|
|
if !strings.HasSuffix(v, "/") {
|
|
return v + "/"
|
|
}
|
|
return v
|
|
}
|
|
|
|
type s3FileInfo struct {
|
|
minio.ObjectInfo
|
|
}
|
|
|
|
func (fi s3FileInfo) IsDir() bool {
|
|
return strings.HasSuffix(fi.ObjectInfo.Key, "/")
|
|
}
|
|
|
|
func (fi s3FileInfo) ModTime() time.Time {
|
|
return fi.ObjectInfo.LastModified
|
|
}
|
|
|
|
func (fi s3FileInfo) Mode() os.FileMode {
|
|
return 0600
|
|
}
|
|
|
|
func (fi s3FileInfo) Name() string {
|
|
return path.Base(fi.ObjectInfo.Key)
|
|
}
|
|
|
|
func (fi s3FileInfo) Size() int64 {
|
|
return fi.ObjectInfo.Size
|
|
}
|
|
|
|
func (fi s3FileInfo) Sys() interface{} {
|
|
return nil
|
|
}
|
|
|
|
type s3File struct {
|
|
p string
|
|
*minio.Object
|
|
info *minio.ObjectInfo
|
|
s3 *s3
|
|
}
|
|
|
|
func (f s3File) isDir() bool {
|
|
return strings.HasSuffix(f.p, "/")
|
|
}
|
|
|
|
func (f s3File) Stat() (os.FileInfo, error) {
|
|
if f.isDir() {
|
|
return s3FileInfo{minio.ObjectInfo{Key: f.p}}, nil
|
|
}
|
|
|
|
return &s3FileInfo{*f.info}, nil
|
|
}
|
|
|
|
func (f s3File) Readdir(count int) ([]os.FileInfo, error) {
|
|
doneCh := make(chan struct{})
|
|
defer close(doneCh)
|
|
|
|
log.Debug("List all files from %s: %s", f.s3.bucket, f.p)
|
|
|
|
objectCh := f.s3.client.ListObjects(f.s3.bucket, buildMinioPath(f.p), false, doneCh)
|
|
var files []os.FileInfo
|
|
for object := range objectCh {
|
|
if object.Err != nil {
|
|
return nil, object.Err
|
|
}
|
|
|
|
// ignore itself
|
|
if object.Key == f.p {
|
|
continue
|
|
}
|
|
|
|
files = append(files, s3FileInfo{object})
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
type s3 struct {
|
|
client *minio.Client
|
|
bucket string
|
|
}
|
|
|
|
func (s *s3) Open(name string) (http.File, error) {
|
|
p := buildMinioPath(name)
|
|
if p == "" {
|
|
p = "/"
|
|
}
|
|
|
|
if p == "/" {
|
|
return s3File{p, nil, nil, s}, nil
|
|
}
|
|
|
|
log.Debug("GetObject from %s: %s", s.bucket, p)
|
|
|
|
obj, err := s.client.GetObject(s.bucket, p, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !strings.HasSuffix(name, "/") {
|
|
info, err := obj.Stat()
|
|
if err != nil {
|
|
if realErr, ok := err.(minio.ErrorResponse); ok && realErr.Code == "NoSuchKey" {
|
|
return nil, os.ErrNotExist
|
|
}
|
|
return nil, err
|
|
}
|
|
return s3File{p, obj, &info, s}, nil
|
|
}
|
|
|
|
return s3File{p, obj, nil, s}, nil
|
|
}
|
|
|
|
func s3FileSystem(endpoint, accessKeyID, secretAccessKey, bucket string, useSSL bool) (http.FileSystem, error) {
|
|
client, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &s3{
|
|
client: client,
|
|
bucket: bucket,
|
|
}, nil
|
|
}
|