1
0
Fork 0
mirror of https://github.com/chrislusf/seaweedfs synced 2024-05-09 04:50:47 +02:00

Compare commits

...

3 commits

Author SHA1 Message Date
Nikita Korolev 9f569ea375
Merge c8922b9c2e into a3dab1fcd0 2024-04-26 19:39:39 -04:00
Kevin Bulteel a3dab1fcd0
feat: upload files directly without multipart/form-data (#5539) 2024-04-26 16:03:40 -07:00
ds c8922b9c2e add sentry support to filer 2024-04-21 01:50:04 +03:00
6 changed files with 135 additions and 10 deletions

View file

@ -10,8 +10,10 @@ import (
"strings"
"time"
sentryhttp "github.com/getsentry/sentry-go/http"
"google.golang.org/grpc/reflection"
"github.com/getsentry/sentry-go"
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb"
@ -264,6 +266,7 @@ func (fo *FilerOptions) startFiler() {
AllowedOrigins: strings.Split(*fo.allowedOrigins, ","),
})
if nfs_err != nil {
sentry.CaptureException(nfs_err)
glog.Fatalf("Filer startup error: %v", nfs_err)
}
@ -272,16 +275,21 @@ func (fo *FilerOptions) startFiler() {
glog.V(0).Infoln("Start Seaweed filer server", util.Version(), "public at", publicListeningAddress)
publicListener, localPublicListener, e := util.NewIpAndLocalListeners(*fo.bindIp, *fo.publicPort, 0)
if e != nil {
sentry.CaptureException(e)
glog.Fatalf("Filer server public listener error on port %d:%v", *fo.publicPort, e)
}
go func() {
if e := http.Serve(publicListener, publicVolumeMux); e != nil {
localHub := sentry.CurrentHub().Clone()
localHub.CaptureException(e)
glog.Fatalf("Volume server fail to serve public: %v", e)
}
}()
if localPublicListener != nil {
go func() {
if e := http.Serve(localPublicListener, publicVolumeMux); e != nil {
localHub := sentry.CurrentHub().Clone()
localHub.CaptureException(e)
glog.Errorf("Volume server fail to serve public: %v", e)
}
}()
@ -294,6 +302,7 @@ func (fo *FilerOptions) startFiler() {
time.Duration(10)*time.Second,
)
if e != nil {
sentry.CaptureException(e)
glog.Fatalf("Filer listener error: %v", e)
}
@ -301,6 +310,7 @@ func (fo *FilerOptions) startFiler() {
grpcPort := *fo.portGrpc
grpcL, grpcLocalL, err := util.NewIpAndLocalListeners(*fo.bindIp, grpcPort, 0)
if err != nil {
sentry.CaptureException(err)
glog.Fatalf("failed to listen on grpc port %d: %v", grpcPort, err)
}
grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.filer"))
@ -311,19 +321,22 @@ func (fo *FilerOptions) startFiler() {
}
go grpcS.Serve(grpcL)
httpS := &http.Server{Handler: defaultMux}
httpS := &http.Server{Handler: sentryhttp.New(sentryhttp.Options{}).Handle(defaultMux)}
if runtime.GOOS != "windows" {
localSocket := *fo.localSocket
if localSocket == "" {
localSocket = fmt.Sprintf("/tmp/seaweedfs-filer-%d.sock", *fo.port)
}
if err := os.Remove(localSocket); err != nil && !os.IsNotExist(err) {
sentry.CaptureException(err)
glog.Fatalf("Failed to remove %s, error: %s", localSocket, err.Error())
}
go func() {
// start on local unix socket
filerSocketListener, err := net.Listen("unix", localSocket)
if err != nil {
localHub := sentry.CurrentHub().Clone()
localHub.CaptureException(err)
glog.Fatalf("Failed to listen on %s: %v", localSocket, err)
}
httpS.Serve(filerSocketListener)
@ -332,11 +345,14 @@ func (fo *FilerOptions) startFiler() {
if filerLocalListener != nil {
go func() {
if err := httpS.Serve(filerLocalListener); err != nil {
localHub := sentry.CurrentHub().Clone()
localHub.CaptureException(e)
glog.Errorf("Filer Fail to serve: %v", e)
}
}()
}
if err := httpS.Serve(filerListener); err != nil {
sentry.CaptureException(err)
glog.Fatalf("Filer Fail to serve: %v", e)
}

View file

@ -25,6 +25,7 @@ import (
"github.com/gorilla/mux"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/s3api"
stats_collect "github.com/seaweedfs/seaweedfs/weed/stats"
@ -236,7 +237,8 @@ func (s3opt *S3Options) startS3Server() bool {
glog.Fatalf("S3 API Server startup error: %v", s3ApiServer_err)
}
httpS := &http.Server{Handler: router}
sentryHandler := sentryhttp.New(sentryhttp.Options{})
httpS := &http.Server{Handler: sentryHandler.Handle(router)}
if *s3opt.portGrpc == 0 {
*s3opt.portGrpc = 10000 + *s3opt.port

View file

@ -9,6 +9,7 @@ import (
"strings"
"time"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
@ -337,7 +338,8 @@ func StartMetricsServer(ip string, port int) {
return
}
http.Handle("/metrics", promhttp.HandlerFor(Gather, promhttp.HandlerOpts{}))
log.Fatal(http.ListenAndServe(JoinHostPort(ip, port), nil))
handler := sentryhttp.New(sentryhttp.Options{}).Handle(http.DefaultServeMux)
log.Fatal(http.ListenAndServe(JoinHostPort(ip, port), handler))
}
func SourceName(port uint32) string {

View file

@ -44,7 +44,15 @@ func ParseUpload(r *http.Request, sizeLimit int64, bytesBuffer *bytes.Buffer) (p
}
if r.Method == "POST" {
e = parseMultipart(r, sizeLimit, pu)
contentType := r.Header.Get("Content-Type")
// If content-type is explicitly set, upload the file without parsing form-data
if contentType != "" && !strings.Contains(contentType, "form-data") {
e = parseRawPost(r, sizeLimit, pu)
} else {
e = parseMultipart(r, sizeLimit, pu)
}
} else {
e = parsePut(r, sizeLimit, pu)
}
@ -205,3 +213,65 @@ func parseMultipart(r *http.Request, sizeLimit int64, pu *ParsedUpload) (e error
return
}
func parseRawPost(r *http.Request, sizeLimit int64, pu *ParsedUpload) (e error) {
defer func() {
if e != nil && r.Body != nil {
io.Copy(io.Discard, r.Body)
r.Body.Close()
}
}()
pu.FileName = r.Header.Get("Content-Disposition")
if pu.FileName != "" && strings.Contains(pu.FileName, "filename=") {
parts := strings.Split(pu.FileName, "filename=")
parts = strings.Split(parts[1], "\"")
pu.FileName = parts[1]
} else {
pu.FileName = ""
}
if pu.FileName != "" {
pu.FileName = path.Base(pu.FileName)
} else {
pu.FileName = path.Base(r.URL.Path)
}
var dataSize int64
dataSize, e = pu.bytesBuffer.ReadFrom(io.LimitReader(r.Body, sizeLimit+1))
if e != nil {
glog.V(0).Infoln("Reading Content [ERROR]", e)
return
}
if dataSize == sizeLimit+1 {
e = fmt.Errorf("file over the limited %d bytes", sizeLimit)
return
}
pu.Data = pu.bytesBuffer.Bytes()
pu.IsChunkedFile, _ = strconv.ParseBool(r.FormValue("cm"))
if !pu.IsChunkedFile {
dotIndex := strings.LastIndex(pu.FileName, ".")
ext, mtype := "", ""
if dotIndex > 0 {
ext = strings.ToLower(pu.FileName[dotIndex:])
mtype = mime.TypeByExtension(ext)
}
contentType := r.Header.Get("Content-Type")
if contentType != "" && contentType != "application/octet-stream" && mtype != contentType {
pu.MimeType = contentType // only return mime type if not deducible
mtype = contentType
}
}
pu.IsGzipped = r.Header.Get("Content-Encoding") == "gzip"
// pu.IsZstd = r.Header.Get("Content-Encoding") == "zstd"
return
}

View file

@ -51,14 +51,13 @@ func main() {
glog.MaxFileCount = 5
flag.Usage = usage
err := sentry.Init(sentry.ClientOptions{
SampleRate: 0.1,
EnableTracing: true,
TracesSampleRate: 0.1,
ProfilesSampleRate: 0.1,
})
err := initSentry()
// Sentry does not return error for empty or unreachable DSN
// Provided DSN might be incorrect, so let's terminate program because we will call sentry later
if err != nil {
fmt.Fprintf(os.Stderr, "sentry.Init: %v", err)
setExitStatus(1)
exit()
}
// Flush buffered events before the program terminates.
// Set the timeout to the maximum duration the program can afford to wait.
@ -128,12 +127,23 @@ var helpTemplate = `{{if .Runnable}}Usage: weed {{.UsageLine}}
{{.Long}}
`
func initSentry() error {
return sentry.Init(sentry.ClientOptions{
SampleRate: 0.1,
EnableTracing: true,
TracesSampleRate: 0.1,
ProfilesSampleRate: 0.1,
})
}
// tmpl executes the given template text on data, writing the result to w.
func tmpl(w io.Writer, text string, data interface{}) {
t := template.New("top")
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
template.Must(t.Parse(text))
if err := t.Execute(w, data); err != nil {
sentry.CaptureException(err)
sentry.Flush(2 * time.Second)
panic(err)
}
}

25
weed/weed_test.go Normal file
View file

@ -0,0 +1,25 @@
package main
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestInitSentryNoDSN(t *testing.T) {
assert.Nil(t, initSentry())
}
func TestInitSentryEmptyDSN(t *testing.T) {
t.Setenv("SENTRY_DSN", "")
assert.Nil(t, initSentry())
}
func TestInitSentryTestDSN(t *testing.T) {
t.Setenv("SENTRY_DSN", "https://example@example.com/123")
assert.Nil(t, initSentry())
}
func TestInitSentryInvalidDSN(t *testing.T) {
t.Setenv("SENTRY_DSN", "https://example.com")
assert.NotNil(t, initSentry())
}