frostfs-s3-gw/cmd/gate/app.go

258 lines
5.6 KiB
Go
Raw Normal View History

2020-07-06 09:18:16 +00:00
package main
import (
"context"
2020-07-12 23:00:47 +00:00
"net"
"net/http"
2020-07-13 10:22:37 +00:00
"os"
2020-07-06 09:18:16 +00:00
"time"
s3auth "github.com/minio/minio/auth"
2020-07-09 09:23:46 +00:00
minio "github.com/minio/minio/legacy"
2020-07-13 10:22:37 +00:00
"github.com/minio/minio/legacy/config"
2020-07-06 09:18:16 +00:00
"github.com/minio/minio/neofs/layer"
"github.com/minio/minio/neofs/metrics"
2020-07-07 11:25:13 +00:00
"github.com/minio/minio/neofs/pool"
2020-07-06 09:18:16 +00:00
"github.com/spf13/viper"
"go.uber.org/zap"
2020-07-07 11:25:13 +00:00
"google.golang.org/grpc/keepalive"
2020-07-06 09:18:16 +00:00
)
type (
App struct {
center *s3auth.Center
cli pool.Pool
log *zap.Logger
cfg *viper.Viper
tls *tlsConfig
obj minio.ObjectLayer
2020-07-06 09:18:16 +00:00
conTimeout time.Duration
reqTimeout time.Duration
2020-07-12 23:00:47 +00:00
reBalance time.Duration
2020-07-06 09:18:16 +00:00
webDone chan struct{}
wrkDone chan struct{}
}
tlsConfig struct {
KeyFile string
CertFile string
}
2020-07-06 09:18:16 +00:00
)
func newApp(l *zap.Logger, v *viper.Viper) *App {
var (
err error
cli pool.Pool
tls *tlsConfig
obj minio.ObjectLayer
reBalance = defaultRebalanceTimer
2020-07-06 09:18:16 +00:00
conTimeout = defaultConnectTimeout
reqTimeout = defaultRequestTimeout
)
center, err := fetchAuthCenter(l, v)
if err != nil {
l.Fatal("failed to initialize auth center", zap.Error(err))
}
uid := center.GetOwnerID()
wif := center.GetWIFString()
if v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile) {
tls = &tlsConfig{
KeyFile: v.GetString(cfgTLSKeyFile),
CertFile: v.GetString(cfgTLSCertFile),
}
}
2020-07-07 11:25:13 +00:00
if v := v.GetDuration(cfgConnectTimeout); v > 0 {
2020-07-06 09:18:16 +00:00
conTimeout = v
}
2020-07-07 11:25:13 +00:00
if v := v.GetDuration(cfgRequestTimeout); v > 0 {
2020-07-06 09:18:16 +00:00
reqTimeout = v
}
2020-07-07 11:25:13 +00:00
poolConfig := &pool.Config{
ConnectionTTL: v.GetDuration(cfgConnectionTTL),
ConnectTimeout: v.GetDuration(cfgConnectTimeout),
RequestTimeout: v.GetDuration(cfgRequestTimeout),
Peers: fetchPeers(l, v),
Logger: l,
PrivateKey: center.GetNeoFSKeyPrivateKey(),
2020-07-07 11:25:13 +00:00
GRPCLogger: gRPCLogger(l),
GRPCVerbose: v.GetBool(cfgGRPCVerbose),
ClientParameters: keepalive.ClientParameters{},
}
2020-07-12 23:00:47 +00:00
if v := v.GetDuration(cfgRebalanceTimer); v > 0 {
reBalance = v
}
2020-07-07 11:25:13 +00:00
if cli, err = pool.New(poolConfig); err != nil {
l.Fatal("could not prepare pool connections", zap.Error(err))
2020-07-06 09:18:16 +00:00
}
{ // should establish connection with NeoFS Storage Nodes
ctx, cancel := context.WithTimeout(context.Background(), conTimeout)
defer cancel()
cli.ReBalance(ctx)
if _, err = cli.GetConnection(ctx); err != nil {
l.Fatal("could not establish connection",
zap.Error(err))
}
}
{ // should prepare object layer
2020-07-13 10:22:37 +00:00
{ // Temporary solution, to resolve problems with MinIO GW access/secret keys:
if err = os.Setenv(config.EnvAccessKey, uid.String()); err != nil {
l.Fatal("could not set "+config.EnvAccessKey, zap.Error(err))
2020-07-13 10:22:37 +00:00
} else if err = os.Setenv(config.EnvSecretKey, wif); err != nil {
l.Fatal("could not set "+config.EnvSecretKey, zap.Error(err))
2020-07-13 10:22:37 +00:00
}
l.Info("used credentials", zap.String("AccessKey", uid.String()), zap.String("SecretKey", wif))
2020-07-13 10:22:37 +00:00
}
if obj, err = layer.NewLayer(l, cli, center); err != nil {
l.Fatal("could not prepare ObjectLayer", zap.Error(err))
2020-07-06 09:18:16 +00:00
}
}
return &App{
center: center,
cli: cli,
log: l,
cfg: v,
obj: obj,
tls: tls,
2020-07-06 09:18:16 +00:00
webDone: make(chan struct{}, 1),
wrkDone: make(chan struct{}, 1),
2020-07-12 23:00:47 +00:00
reBalance: reBalance,
2020-07-06 09:18:16 +00:00
conTimeout: conTimeout,
reqTimeout: reqTimeout,
}
}
2020-07-12 23:00:47 +00:00
func (a *App) Wait() {
2020-07-06 09:18:16 +00:00
a.log.Info("application started")
2020-07-12 23:00:47 +00:00
2020-07-06 09:18:16 +00:00
select {
case <-a.wrkDone: // wait for worker is stopped
<-a.webDone
case <-a.webDone: // wait for web-server is stopped
<-a.wrkDone
}
2020-07-12 23:00:47 +00:00
a.log.Info("application finished")
2020-07-06 09:18:16 +00:00
}
func (a *App) Server(ctx context.Context) {
2020-07-12 23:00:47 +00:00
var (
err error
lis net.Listener
lic net.ListenConfig
srv = new(http.Server)
2020-07-12 23:00:47 +00:00
addr = a.cfg.GetString(cfgListenAddress)
)
if lis, err = lic.Listen(ctx, "tcp", addr); err != nil {
a.log.Fatal("could not prepare listener",
zap.Error(err))
}
router := newS3Router()
2020-07-12 23:00:47 +00:00
// Attach app-specific routes:
2020-07-16 15:33:47 +00:00
attachNewUserAuth(router, a.center, a.log)
attachHealthy(router, a.cli)
attachMetrics(router, a.cfg, a.log)
attachProfiler(router, a.cfg, a.log)
{ // Example for metrics.Middleware and metrics.APIStats
r := router.PathPrefix("/test-metrics").Subrouter()
r.Handle("/foo", metrics.APIStats("foo", func(w http.ResponseWriter, r *http.Request) {
// do something
}))
m := r.PathPrefix("/bar").Subrouter()
m.Use(metrics.Middleware)
m.Handle("", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// do something
}))
}
// Attach S3 API:
r := router.PathPrefix(minio.SlashSeparator).Subrouter()
r.Use(metrics.Middleware)
minio.AttachS3API(r, a.obj, a.log)
// Use mux.Router as http.Handler
srv.Handler = router
2020-07-12 23:00:47 +00:00
go func() {
a.log.Info("starting server",
zap.String("bind", addr))
switch a.tls {
case nil:
if err = srv.Serve(lis); err != nil && err != http.ErrServerClosed {
a.log.Fatal("listen and serve",
zap.Error(err))
}
default:
a.log.Info("using certificate",
zap.String("key", a.tls.KeyFile),
zap.String("cert", a.tls.CertFile))
2020-07-13 11:23:23 +00:00
if err = srv.ServeTLS(lis, a.tls.CertFile, a.tls.KeyFile); err != nil && err != http.ErrServerClosed {
a.log.Fatal("listen and serve",
zap.Error(err))
}
2020-07-12 23:00:47 +00:00
}
2020-07-06 09:18:16 +00:00
}()
2020-07-12 23:00:47 +00:00
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout)
defer cancel()
a.log.Info("stopping server",
zap.Error(srv.Shutdown(ctx)))
close(a.webDone)
2020-07-06 09:18:16 +00:00
}
func (a *App) Worker(ctx context.Context) {
2020-07-12 23:00:47 +00:00
tick := time.NewTimer(a.reBalance)
loop:
for {
select {
case <-ctx.Done():
break loop
case <-tick.C:
ctx, cancel := context.WithTimeout(ctx, a.conTimeout)
a.cli.ReBalance(ctx)
cancel()
tick.Reset(a.reBalance)
}
}
tick.Stop()
a.cli.Close()
a.log.Info("stopping worker")
close(a.wrkDone)
2020-07-06 09:18:16 +00:00
}