2020-07-06 09:18:16 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-09-12 13:46:55 +00:00
|
|
|
"crypto/tls"
|
2021-06-22 13:25:29 +00:00
|
|
|
"encoding/hex"
|
2022-09-12 13:46:55 +00:00
|
|
|
"errors"
|
2022-04-25 09:57:58 +00:00
|
|
|
"fmt"
|
2020-07-12 23:00:47 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2022-09-12 09:45:54 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2021-10-04 14:32:35 +00:00
|
|
|
"strconv"
|
2022-09-12 12:30:51 +00:00
|
|
|
"sync"
|
2022-09-12 09:45:54 +00:00
|
|
|
"syscall"
|
2021-09-08 07:08:56 +00:00
|
|
|
"time"
|
2020-07-06 09:18:16 +00:00
|
|
|
|
2022-07-26 13:29:07 +00:00
|
|
|
"github.com/gorilla/mux"
|
2021-06-22 14:11:44 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2021-05-18 11:10:08 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/auth"
|
2021-08-16 09:58:09 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
2021-05-18 11:10:08 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/handler"
|
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
2022-02-04 10:05:36 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/notifications"
|
2022-01-11 10:09:34 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/resolver"
|
2022-03-01 19:02:24 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/internal/neofs"
|
2021-12-06 06:58:15 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
|
2021-06-24 15:21:34 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/internal/wallet"
|
2021-08-27 12:48:59 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
2020-07-06 09:18:16 +00:00
|
|
|
"github.com/spf13/viper"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2021-05-13 20:25:31 +00:00
|
|
|
// App is the main application structure.
|
2020-07-06 09:18:16 +00:00
|
|
|
App struct {
|
2022-09-12 13:46:55 +00:00
|
|
|
ctr auth.Center
|
|
|
|
log *zap.Logger
|
|
|
|
cfg *viper.Viper
|
|
|
|
pool *pool.Pool
|
|
|
|
obj layer.Client
|
|
|
|
api api.Handler
|
2020-07-06 09:18:16 +00:00
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
metrics *appMetrics
|
|
|
|
bucketResolver *resolver.BucketResolver
|
|
|
|
tlsProvider *certProvider
|
2022-07-21 14:42:24 +00:00
|
|
|
|
2020-07-22 13:02:32 +00:00
|
|
|
maxClients api.MaxClients
|
|
|
|
|
2020-07-06 09:18:16 +00:00
|
|
|
webDone chan struct{}
|
|
|
|
wrkDone chan struct{}
|
2022-09-12 09:45:54 +00:00
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
services []*Service
|
2022-09-12 09:45:54 +00:00
|
|
|
settings *appSettings
|
|
|
|
}
|
|
|
|
|
|
|
|
appSettings struct {
|
|
|
|
LogLevel zap.AtomicLevel
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger struct {
|
|
|
|
logger *zap.Logger
|
|
|
|
lvl zap.AtomicLevel
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
2020-07-13 15:50:11 +00:00
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
certProvider struct {
|
|
|
|
Enabled bool
|
|
|
|
|
|
|
|
mu sync.RWMutex
|
|
|
|
certPath string
|
|
|
|
keyPath string
|
|
|
|
cert *tls.Certificate
|
2020-07-13 15:50:11 +00:00
|
|
|
}
|
2022-07-21 14:42:24 +00:00
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
appMetrics struct {
|
|
|
|
logger *zap.Logger
|
|
|
|
provider GateMetricsCollector
|
|
|
|
mu sync.RWMutex
|
|
|
|
enabled bool
|
|
|
|
}
|
|
|
|
|
2022-07-21 14:42:24 +00:00
|
|
|
GateMetricsCollector interface {
|
|
|
|
SetHealth(int32)
|
2022-09-12 12:30:51 +00:00
|
|
|
Unregister()
|
2022-07-21 14:42:24 +00:00
|
|
|
}
|
2020-07-06 09:18:16 +00:00
|
|
|
)
|
|
|
|
|
2022-09-12 09:45:54 +00:00
|
|
|
func newApp(ctx context.Context, log *Logger, v *viper.Viper) *App {
|
|
|
|
l := log.logger
|
|
|
|
|
2020-07-06 09:18:16 +00:00
|
|
|
var (
|
2021-06-22 14:11:44 +00:00
|
|
|
key *keys.PrivateKey
|
2020-10-13 09:31:23 +00:00
|
|
|
err error
|
|
|
|
caller api.Handler
|
2020-11-24 07:09:58 +00:00
|
|
|
ctr auth.Center
|
2020-10-13 09:31:23 +00:00
|
|
|
obj layer.Client
|
2022-03-25 11:13:54 +00:00
|
|
|
nc *notifications.Controller
|
2020-10-13 09:31:23 +00:00
|
|
|
|
2022-04-07 14:36:44 +00:00
|
|
|
prmPool pool.InitParameters
|
2020-10-13 09:31:23 +00:00
|
|
|
|
2022-04-14 15:09:57 +00:00
|
|
|
reBalance = defaultRebalanceInterval
|
2020-07-06 09:18:16 +00:00
|
|
|
conTimeout = defaultConnectTimeout
|
2022-04-14 15:09:57 +00:00
|
|
|
hckTimeout = defaultHealthcheckTimeout
|
2020-07-22 13:02:32 +00:00
|
|
|
|
|
|
|
maxClientsCount = defaultMaxClientsCount
|
|
|
|
maxClientsDeadline = defaultMaxClientsDeadline
|
2022-07-29 06:26:11 +00:00
|
|
|
poolErrorThreshold = defaultPoolErrorThreshold
|
2020-07-06 09:18:16 +00:00
|
|
|
)
|
2020-07-13 15:50:11 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-04-14 15:09:57 +00:00
|
|
|
if v := v.GetDuration(cfgHealthcheckTimeout); v > 0 {
|
|
|
|
hckTimeout = v
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 13:02:32 +00:00
|
|
|
if v := v.GetInt(cfgMaxClientsCount); v > 0 {
|
|
|
|
maxClientsCount = v
|
|
|
|
}
|
|
|
|
|
|
|
|
if v := v.GetDuration(cfgMaxClientsDeadline); v > 0 {
|
|
|
|
maxClientsDeadline = v
|
|
|
|
}
|
|
|
|
|
2022-04-14 15:09:57 +00:00
|
|
|
if v := v.GetDuration(cfgRebalanceInterval); v > 0 {
|
2020-10-13 09:31:23 +00:00
|
|
|
reBalance = v
|
|
|
|
}
|
|
|
|
|
2022-07-29 06:26:11 +00:00
|
|
|
if v := v.GetUint32(cfgPoolErrorThreshold); v > 0 {
|
|
|
|
poolErrorThreshold = v
|
|
|
|
}
|
|
|
|
|
2021-06-24 15:21:34 +00:00
|
|
|
password := wallet.GetPassword(v, cfgWalletPassphrase)
|
2022-07-28 13:26:42 +00:00
|
|
|
if key, err = wallet.GetKeyFromPath(v.GetString(cfgWalletPath), v.GetString(cfgWalletAddress), password); err != nil {
|
2021-06-22 13:25:29 +00:00
|
|
|
l.Fatal("could not load NeoFS private key", zap.Error(err))
|
2020-10-13 09:31:23 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
l.Info("using credentials", zap.String("NeoFS", hex.EncodeToString(key.PublicKey().Bytes())))
|
2020-11-24 07:09:58 +00:00
|
|
|
|
2022-04-07 14:36:44 +00:00
|
|
|
prmPool.SetKey(&key.PrivateKey)
|
|
|
|
prmPool.SetNodeDialTimeout(conTimeout)
|
2022-04-14 15:09:57 +00:00
|
|
|
prmPool.SetHealthcheckTimeout(hckTimeout)
|
2022-07-29 06:26:11 +00:00
|
|
|
prmPool.SetErrorThreshold(poolErrorThreshold)
|
2022-04-07 14:36:44 +00:00
|
|
|
prmPool.SetClientRebalanceInterval(reBalance)
|
|
|
|
for _, peer := range fetchPeers(l, v) {
|
|
|
|
prmPool.AddNode(peer)
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
2022-04-07 14:36:44 +00:00
|
|
|
|
|
|
|
conns, err := pool.NewPool(prmPool)
|
2021-05-26 16:48:27 +00:00
|
|
|
if err != nil {
|
|
|
|
l.Fatal("failed to create connection pool", zap.Error(err))
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
2020-10-19 01:05:28 +00:00
|
|
|
|
2022-04-07 14:36:44 +00:00
|
|
|
if err = conns.Dial(ctx); err != nil {
|
|
|
|
l.Fatal("failed to dial connection pool", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2021-10-19 15:08:07 +00:00
|
|
|
// prepare random key for anonymous requests
|
|
|
|
randomKey, err := keys.NewPrivateKey()
|
|
|
|
if err != nil {
|
|
|
|
l.Fatal("couldn't generate random key", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2022-01-11 10:09:34 +00:00
|
|
|
resolveCfg := &resolver.Config{
|
2022-04-25 09:57:58 +00:00
|
|
|
NeoFS: neofs.NewResolverNeoFS(conns),
|
|
|
|
RPCAddress: v.GetString(cfgRPCEndpoint),
|
2022-01-11 10:09:34 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
order := v.GetStringSlice(cfgResolveOrder)
|
|
|
|
if resolveCfg.RPCAddress == "" {
|
|
|
|
order = remove(order, resolver.NNSResolver)
|
|
|
|
l.Warn(fmt.Sprintf("resolver '%s' won't be used since '%s' isn't provided", resolver.NNSResolver, cfgRPCEndpoint))
|
2022-01-11 10:09:34 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
bucketResolver, err := resolver.NewBucketResolver(order, resolveCfg)
|
2022-01-11 10:09:34 +00:00
|
|
|
if err != nil {
|
|
|
|
l.Fatal("failed to form resolver", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2022-04-22 07:18:21 +00:00
|
|
|
treeServiceEndpoint := v.GetString(cfgTreeServiceEndpoint)
|
2022-05-18 13:40:09 +00:00
|
|
|
treeService, err := neofs.NewTreeClient(treeServiceEndpoint, key)
|
2022-04-22 07:18:21 +00:00
|
|
|
if err != nil {
|
|
|
|
l.Fatal("failed to create tree service", zap.Error(err))
|
|
|
|
}
|
|
|
|
l.Info("init tree service", zap.String("endpoint", treeServiceEndpoint))
|
|
|
|
|
2021-11-22 09:16:05 +00:00
|
|
|
layerCfg := &layer.Config{
|
|
|
|
Caches: getCacheOptions(v, l),
|
|
|
|
AnonKey: layer.AnonymousKey{
|
|
|
|
Key: randomKey,
|
|
|
|
},
|
2022-04-22 07:18:21 +00:00
|
|
|
Resolver: bucketResolver,
|
|
|
|
TreeService: treeService,
|
2021-11-22 09:16:05 +00:00
|
|
|
}
|
2021-08-16 09:58:09 +00:00
|
|
|
|
2020-11-24 07:09:58 +00:00
|
|
|
// prepare object layer
|
2022-03-25 10:28:39 +00:00
|
|
|
obj = layer.NewLayer(l, neofs.NewNeoFS(conns), layerCfg)
|
2020-11-24 07:09:58 +00:00
|
|
|
|
2022-03-25 11:13:54 +00:00
|
|
|
if v.GetBool(cfgEnableNATS) {
|
|
|
|
nopts := getNotificationsOptions(v, l)
|
|
|
|
nc, err = notifications.NewController(nopts, l)
|
|
|
|
if err != nil {
|
|
|
|
l.Fatal("failed to enable notifications", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = obj.Initialize(ctx, nc); err != nil {
|
|
|
|
l.Fatal("couldn't initialize layer", zap.Error(err))
|
|
|
|
}
|
2022-03-05 06:58:54 +00:00
|
|
|
}
|
|
|
|
|
2020-11-24 07:09:58 +00:00
|
|
|
// prepare auth center
|
2022-08-31 22:43:36 +00:00
|
|
|
ctr = auth.New(neofs.NewAuthmateNeoFS(conns), key, v.GetStringSlice(cfgAllowedAccessKeyIDPrefixes), getAccessBoxCacheConfig(v, l))
|
2021-08-23 18:39:15 +00:00
|
|
|
handlerOptions := getHandlerOptions(v, l)
|
|
|
|
|
2022-04-29 13:08:22 +00:00
|
|
|
if caller, err = handler.New(l, obj, nc, handlerOptions); err != nil {
|
2020-08-06 10:50:04 +00:00
|
|
|
l.Fatal("could not initialize API handler", zap.Error(err))
|
2020-07-24 16:10:41 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
app := &App{
|
2022-09-12 13:46:55 +00:00
|
|
|
ctr: ctr,
|
|
|
|
log: l,
|
|
|
|
cfg: v,
|
|
|
|
pool: conns,
|
|
|
|
obj: obj,
|
|
|
|
api: caller,
|
2020-07-06 09:18:16 +00:00
|
|
|
|
|
|
|
webDone: make(chan struct{}, 1),
|
|
|
|
wrkDone: make(chan struct{}, 1),
|
|
|
|
|
2020-07-22 13:02:32 +00:00
|
|
|
maxClients: api.NewMaxClientsMiddleware(maxClientsCount, maxClientsDeadline),
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
2022-09-12 12:30:51 +00:00
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
app.initMetrics()
|
|
|
|
app.initResolver()
|
|
|
|
app.initTLSProvider()
|
2022-09-12 12:30:51 +00:00
|
|
|
|
|
|
|
return app
|
|
|
|
}
|
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
func (a *App) initMetrics() {
|
|
|
|
gateMetricsProvider := newGateMetrics(neofs.NewPoolStatistic(a.pool))
|
2022-09-12 12:30:51 +00:00
|
|
|
a.metrics = newAppMetrics(a.log, gateMetricsProvider, a.cfg.GetBool(cfgPrometheusEnabled))
|
|
|
|
}
|
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
func (a *App) initResolver() {
|
|
|
|
var err error
|
|
|
|
a.bucketResolver, err = resolver.NewBucketResolver(a.getResolverConfig())
|
|
|
|
if err != nil {
|
|
|
|
a.log.Fatal("failed to create resolver", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *App) initTLSProvider() {
|
|
|
|
a.tlsProvider = &certProvider{
|
|
|
|
Enabled: a.cfg.IsSet(cfgTLSCertFile) || a.cfg.IsSet(cfgTLSKeyFile),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *App) getResolverConfig() ([]string, *resolver.Config) {
|
|
|
|
resolveCfg := &resolver.Config{
|
|
|
|
NeoFS: neofs.NewResolverNeoFS(a.pool),
|
|
|
|
RPCAddress: a.cfg.GetString(cfgRPCEndpoint),
|
|
|
|
}
|
|
|
|
|
|
|
|
order := a.cfg.GetStringSlice(cfgResolveOrder)
|
|
|
|
if resolveCfg.RPCAddress == "" {
|
|
|
|
order = remove(order, resolver.NNSResolver)
|
|
|
|
a.log.Warn(fmt.Sprintf("resolver '%s' won't be used since '%s' isn't provided", resolver.NNSResolver, cfgRPCEndpoint))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(order) == 0 {
|
|
|
|
a.log.Info("container resolver will be disabled because of resolvers 'resolver_order' is empty")
|
|
|
|
}
|
|
|
|
|
|
|
|
return order, resolveCfg
|
|
|
|
}
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
func newAppMetrics(logger *zap.Logger, provider GateMetricsCollector, enabled bool) *appMetrics {
|
|
|
|
if !enabled {
|
|
|
|
logger.Warn("metrics are disabled")
|
|
|
|
}
|
|
|
|
return &appMetrics{
|
|
|
|
logger: logger,
|
|
|
|
provider: provider,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *appMetrics) SetEnabled(enabled bool) {
|
|
|
|
if !enabled {
|
|
|
|
m.logger.Warn("metrics are disabled")
|
|
|
|
}
|
|
|
|
|
|
|
|
m.mu.Lock()
|
|
|
|
m.enabled = enabled
|
|
|
|
m.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *appMetrics) SetHealth(status int32) {
|
|
|
|
m.mu.RLock()
|
|
|
|
if !m.enabled {
|
|
|
|
m.mu.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m.mu.RUnlock()
|
|
|
|
|
|
|
|
m.provider.SetHealth(status)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *appMetrics) Shutdown() {
|
|
|
|
m.mu.Lock()
|
|
|
|
if m.enabled {
|
|
|
|
m.provider.SetHealth(0)
|
|
|
|
m.enabled = false
|
|
|
|
}
|
|
|
|
m.provider.Unregister()
|
|
|
|
m.mu.Unlock()
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
func (p *certProvider) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
|
|
if !p.Enabled {
|
|
|
|
return nil, errors.New("cert provider: disabled")
|
|
|
|
}
|
|
|
|
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
return p.cert, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *certProvider) UpdateCert(certPath, keyPath string) error {
|
|
|
|
if !p.Enabled {
|
|
|
|
return fmt.Errorf("tls disabled")
|
|
|
|
}
|
|
|
|
|
|
|
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot load TLS key pair from certFile '%s' and keyFile '%s': %w", certPath, keyPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.mu.Lock()
|
|
|
|
p.certPath = certPath
|
|
|
|
p.keyPath = keyPath
|
|
|
|
p.cert = &cert
|
|
|
|
p.mu.Unlock()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *certProvider) FilePaths() (string, string) {
|
|
|
|
if !p.Enabled {
|
|
|
|
return "", ""
|
|
|
|
}
|
|
|
|
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
return p.certPath, p.keyPath
|
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
func remove(list []string, element string) []string {
|
|
|
|
for i, item := range list {
|
|
|
|
if item == element {
|
|
|
|
return append(list[:i], list[i+1:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// Wait waits for an application to finish.
|
2022-03-29 09:39:49 +00:00
|
|
|
//
|
|
|
|
// Pre-logs a message about the launch of the application mentioning its
|
2022-04-13 16:56:58 +00:00
|
|
|
// version (version.Version) and its name (neofs-s3-gw). At the end, it writes
|
|
|
|
// about the stop to the log.
|
2020-07-12 23:00:47 +00:00
|
|
|
func (a *App) Wait() {
|
2022-03-29 09:39:49 +00:00
|
|
|
a.log.Info("application started",
|
|
|
|
zap.String("name", "neofs-s3-gw"),
|
|
|
|
zap.String("version", version.Version),
|
|
|
|
)
|
2020-07-12 23:00:47 +00:00
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
a.setHealthStatus()
|
2022-07-21 14:42:24 +00:00
|
|
|
|
2021-05-26 16:48:27 +00:00
|
|
|
<-a.webDone // wait for web-server to be stopped
|
2020-07-12 23:00:47 +00:00
|
|
|
|
|
|
|
a.log.Info("application finished")
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
func (a *App) setHealthStatus() {
|
|
|
|
a.metrics.SetHealth(1)
|
|
|
|
}
|
|
|
|
|
2022-09-12 09:45:54 +00:00
|
|
|
// Serve runs HTTP server to handle S3 API requests.
|
|
|
|
func (a *App) Serve(ctx context.Context) {
|
2020-07-13 09:51:21 +00:00
|
|
|
// Attach S3 API:
|
2022-08-30 10:52:37 +00:00
|
|
|
domains := a.cfg.GetStringSlice(cfgListenDomains)
|
2022-09-12 13:46:55 +00:00
|
|
|
a.log.Info("fetch domains, prepare to use API", zap.Strings("domains", domains))
|
|
|
|
router := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
2020-12-10 15:11:45 +00:00
|
|
|
api.Attach(router, domains, a.maxClients, a.api, a.ctr, a.log)
|
2020-07-13 09:51:21 +00:00
|
|
|
|
|
|
|
// Use mux.Router as http.Handler
|
2022-09-12 13:46:55 +00:00
|
|
|
srv := new(http.Server)
|
2020-07-13 09:51:21 +00:00
|
|
|
srv.Handler = router
|
2020-10-23 00:12:37 +00:00
|
|
|
srv.ErrorLog = zap.NewStdLog(a.log)
|
2020-07-12 23:00:47 +00:00
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
a.startServices()
|
2022-07-26 13:29:07 +00:00
|
|
|
|
2020-07-12 23:00:47 +00:00
|
|
|
go func() {
|
2022-09-12 13:46:55 +00:00
|
|
|
addr := a.cfg.GetString(cfgListenAddress)
|
|
|
|
a.log.Info("starting server", zap.String("bind", addr))
|
|
|
|
|
|
|
|
var lic net.ListenConfig
|
|
|
|
ln, err := lic.Listen(ctx, "tcp", addr)
|
|
|
|
if err != nil {
|
|
|
|
a.log.Fatal("could not prepare listener", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.tlsProvider.Enabled {
|
|
|
|
certFile := a.cfg.GetString(cfgTLSCertFile)
|
|
|
|
keyFile := a.cfg.GetString(cfgTLSKeyFile)
|
|
|
|
|
|
|
|
a.log.Info("using certificate", zap.String("cert", certFile), zap.String("key", keyFile))
|
|
|
|
if err = a.tlsProvider.UpdateCert(certFile, keyFile); err != nil {
|
|
|
|
a.log.Fatal("failed to update cert", zap.Error(err))
|
2020-07-13 15:50:11 +00:00
|
|
|
}
|
2022-09-12 13:46:55 +00:00
|
|
|
|
|
|
|
lnTLS := tls.NewListener(ln, &tls.Config{
|
|
|
|
GetCertificate: a.tlsProvider.GetCertificate,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err = srv.ServeTLS(lnTLS, certFile, keyFile); err != nil && err != http.ErrServerClosed {
|
|
|
|
a.log.Fatal("listen and serve", zap.Error(err))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err = srv.Serve(ln); err != nil && err != http.ErrServerClosed {
|
|
|
|
a.log.Fatal("listen and serve", zap.Error(err))
|
2020-07-13 15:50:11 +00:00
|
|
|
}
|
2020-07-12 23:00:47 +00:00
|
|
|
}
|
2020-07-06 09:18:16 +00:00
|
|
|
}()
|
2020-07-12 23:00:47 +00:00
|
|
|
|
2022-09-12 09:45:54 +00:00
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGHUP)
|
|
|
|
|
|
|
|
LOOP:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
break LOOP
|
|
|
|
case <-sigs:
|
|
|
|
a.configReload()
|
|
|
|
}
|
|
|
|
}
|
2020-07-12 23:00:47 +00:00
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
ctx, cancel := shutdownContext()
|
2020-07-12 23:00:47 +00:00
|
|
|
defer cancel()
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
a.log.Info("stopping server", zap.Error(srv.Shutdown(ctx)))
|
|
|
|
|
|
|
|
a.metrics.Shutdown()
|
|
|
|
a.stopServices()
|
2020-07-12 23:00:47 +00:00
|
|
|
|
|
|
|
close(a.webDone)
|
2020-07-06 09:18:16 +00:00
|
|
|
}
|
2021-08-16 09:58:09 +00:00
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
func shutdownContext() (context.Context, context.CancelFunc) {
|
|
|
|
return context.WithTimeout(context.Background(), defaultShutdownTimeout)
|
|
|
|
}
|
|
|
|
|
2022-09-12 09:45:54 +00:00
|
|
|
func (a *App) configReload() {
|
|
|
|
a.log.Info("SIGHUP config reload started")
|
|
|
|
|
|
|
|
if !a.cfg.IsSet(cmdConfig) {
|
|
|
|
a.log.Warn("failed to reload config because it's missed")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := readConfig(a.cfg); err != nil {
|
|
|
|
a.log.Warn("failed to reload config", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-09-12 13:46:55 +00:00
|
|
|
if err := a.bucketResolver.UpdateResolvers(a.getResolverConfig()); err != nil {
|
|
|
|
a.log.Warn("failed to reload resolvers", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.tlsProvider.UpdateCert(a.cfg.GetString(cfgTLSCertFile), a.cfg.GetString(cfgTLSKeyFile)); err != nil {
|
|
|
|
a.log.Warn("failed to reload TLS certs", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
a.stopServices()
|
|
|
|
a.startServices()
|
|
|
|
|
2022-09-12 09:45:54 +00:00
|
|
|
a.updateSettings()
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
a.metrics.SetEnabled(a.cfg.GetBool(cfgPrometheusEnabled))
|
|
|
|
a.setHealthStatus()
|
|
|
|
|
2022-09-12 09:45:54 +00:00
|
|
|
a.log.Info("SIGHUP config reload completed")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *App) updateSettings() {
|
|
|
|
if lvl, err := getLogLevel(a.cfg); err != nil {
|
|
|
|
a.log.Warn("log level won't be updated", zap.Error(err))
|
|
|
|
} else {
|
|
|
|
a.settings.LogLevel.SetLevel(lvl)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-12 12:30:51 +00:00
|
|
|
func (a *App) startServices() {
|
|
|
|
pprofService := NewPprofService(a.cfg, a.log)
|
|
|
|
a.services = append(a.services, pprofService)
|
|
|
|
go pprofService.Start()
|
|
|
|
|
|
|
|
prometheusService := NewPrometheusService(a.cfg, a.log)
|
|
|
|
a.services = append(a.services, prometheusService)
|
|
|
|
go prometheusService.Start()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *App) stopServices() {
|
|
|
|
ctx, cancel := shutdownContext()
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
for _, svc := range a.services {
|
|
|
|
svc.ShutDown(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-04 10:05:36 +00:00
|
|
|
func getNotificationsOptions(v *viper.Viper, l *zap.Logger) *notifications.Options {
|
|
|
|
cfg := notifications.Options{}
|
|
|
|
cfg.URL = v.GetString(cfgNATSEndpoint)
|
|
|
|
cfg.Timeout = v.GetDuration(cfgNATSTimeout)
|
|
|
|
if cfg.Timeout <= 0 {
|
|
|
|
l.Error("invalid lifetime, using default value (in seconds)",
|
|
|
|
zap.String("parameter", cfgNATSTimeout),
|
|
|
|
zap.Duration("value in config", cfg.Timeout),
|
|
|
|
zap.Duration("default", notifications.DefaultTimeout))
|
|
|
|
cfg.Timeout = notifications.DefaultTimeout
|
|
|
|
}
|
|
|
|
cfg.TLSCertFilepath = v.GetString(cfgNATSTLSCertFile)
|
|
|
|
cfg.TLSAuthPrivateKeyFilePath = v.GetString(cfgNATSAuthPrivateKeyFile)
|
|
|
|
cfg.RootCAFiles = v.GetStringSlice(cfgNATSRootCAFiles)
|
|
|
|
|
|
|
|
return &cfg
|
|
|
|
}
|
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
func getCacheOptions(v *viper.Viper, l *zap.Logger) *layer.CachesConfig {
|
2022-06-06 08:01:12 +00:00
|
|
|
cacheCfg := layer.DefaultCachesConfigs(l)
|
2021-09-10 06:56:56 +00:00
|
|
|
|
|
|
|
cacheCfg.Objects.Lifetime = getLifetime(v, l, cfgObjectsCacheLifetime, cacheCfg.Objects.Lifetime)
|
|
|
|
cacheCfg.Objects.Size = getSize(v, l, cfgObjectsCacheSize, cacheCfg.Objects.Size)
|
|
|
|
|
|
|
|
cacheCfg.ObjectsList.Lifetime = getLifetime(v, l, cfgListObjectsCacheLifetime, cacheCfg.ObjectsList.Lifetime)
|
|
|
|
cacheCfg.ObjectsList.Size = getSize(v, l, cfgListObjectsCacheSize, cacheCfg.ObjectsList.Size)
|
2021-08-16 09:58:09 +00:00
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
cacheCfg.Buckets.Lifetime = getLifetime(v, l, cfgBucketsCacheLifetime, cacheCfg.Buckets.Lifetime)
|
|
|
|
cacheCfg.Buckets.Size = getSize(v, l, cfgBucketsCacheSize, cacheCfg.Buckets.Size)
|
2021-08-16 09:58:09 +00:00
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
cacheCfg.Names.Lifetime = getLifetime(v, l, cfgNamesCacheLifetime, cacheCfg.Names.Lifetime)
|
|
|
|
cacheCfg.Names.Size = getSize(v, l, cfgNamesCacheSize, cacheCfg.Names.Size)
|
2021-08-16 09:58:09 +00:00
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
cacheCfg.System.Lifetime = getLifetime(v, l, cfgSystemLifetimeSize, cacheCfg.System.Lifetime)
|
|
|
|
cacheCfg.System.Size = getSize(v, l, cfgSystemCacheSize, cacheCfg.System.Size)
|
|
|
|
|
|
|
|
return cacheCfg
|
2021-09-08 07:08:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getLifetime(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue time.Duration) time.Duration {
|
|
|
|
if v.IsSet(cfgEntry) {
|
|
|
|
lifetime := v.GetDuration(cfgEntry)
|
2021-08-16 09:58:09 +00:00
|
|
|
if lifetime <= 0 {
|
2021-09-08 07:08:56 +00:00
|
|
|
l.Error("invalid lifetime, using default value (in seconds)",
|
|
|
|
zap.String("parameter", cfgEntry),
|
2021-08-16 09:58:09 +00:00
|
|
|
zap.Duration("value in config", lifetime),
|
2021-09-08 07:08:56 +00:00
|
|
|
zap.Duration("default", defaultValue))
|
2021-08-16 09:58:09 +00:00
|
|
|
} else {
|
2021-09-08 07:08:56 +00:00
|
|
|
return lifetime
|
2021-08-16 09:58:09 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-08 07:08:56 +00:00
|
|
|
return defaultValue
|
|
|
|
}
|
2021-09-01 16:10:31 +00:00
|
|
|
|
2021-09-08 07:08:56 +00:00
|
|
|
func getSize(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue int) int {
|
|
|
|
if v.IsSet(cfgEntry) {
|
|
|
|
size := v.GetInt(cfgEntry)
|
2021-09-01 16:10:31 +00:00
|
|
|
if size <= 0 {
|
|
|
|
l.Error("invalid cache size, using default value",
|
2021-09-08 07:08:56 +00:00
|
|
|
zap.String("parameter", cfgEntry),
|
2021-09-01 16:10:31 +00:00
|
|
|
zap.Int("value in config", size),
|
2021-09-08 07:08:56 +00:00
|
|
|
zap.Int("default", defaultValue))
|
2021-09-01 16:10:31 +00:00
|
|
|
} else {
|
2021-09-08 07:08:56 +00:00
|
|
|
return size
|
2021-09-01 16:10:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-08 07:08:56 +00:00
|
|
|
return defaultValue
|
|
|
|
}
|
2021-09-01 16:10:31 +00:00
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
func getAccessBoxCacheConfig(v *viper.Viper, l *zap.Logger) *cache.Config {
|
2022-06-06 08:01:12 +00:00
|
|
|
cacheCfg := cache.DefaultAccessBoxConfig(l)
|
2021-09-08 07:08:56 +00:00
|
|
|
|
|
|
|
cacheCfg.Lifetime = getLifetime(v, l, cfgAccessBoxCacheLifetime, cacheCfg.Lifetime)
|
|
|
|
cacheCfg.Size = getSize(v, l, cfgAccessBoxCacheSize, cacheCfg.Size)
|
|
|
|
|
|
|
|
return cacheCfg
|
2021-08-16 09:58:09 +00:00
|
|
|
}
|
2021-08-23 18:39:15 +00:00
|
|
|
|
|
|
|
func getHandlerOptions(v *viper.Viper, l *zap.Logger) *handler.Config {
|
|
|
|
var (
|
2022-08-11 22:48:56 +00:00
|
|
|
cfg handler.Config
|
|
|
|
err error
|
|
|
|
policyStr = handler.DefaultPolicy
|
|
|
|
defaultMaxAge = handler.DefaultMaxAge
|
|
|
|
setCopiesNumber = handler.DefaultCopiesNumber
|
2021-08-23 18:39:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if v.IsSet(cfgDefaultPolicy) {
|
|
|
|
policyStr = v.GetString(cfgDefaultPolicy)
|
|
|
|
}
|
|
|
|
|
2022-06-15 19:31:41 +00:00
|
|
|
if err = cfg.DefaultPolicy.DecodeString(policyStr); err != nil {
|
2021-08-23 18:39:15 +00:00
|
|
|
l.Fatal("couldn't parse container default policy",
|
|
|
|
zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2021-10-04 14:32:35 +00:00
|
|
|
if v.IsSet(cfgDefaultMaxAge) {
|
|
|
|
defaultMaxAge = v.GetInt(cfgDefaultMaxAge)
|
|
|
|
|
|
|
|
if defaultMaxAge <= 0 && defaultMaxAge != -1 {
|
|
|
|
l.Fatal("invalid defaultMaxAge",
|
|
|
|
zap.String("parameter", cfgDefaultMaxAge),
|
|
|
|
zap.String("value in config", strconv.Itoa(defaultMaxAge)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 22:48:56 +00:00
|
|
|
if val := v.GetUint32(cfgSetCopiesNumber); val > 0 {
|
|
|
|
setCopiesNumber = val
|
|
|
|
}
|
|
|
|
|
2021-10-04 14:32:35 +00:00
|
|
|
cfg.DefaultMaxAge = defaultMaxAge
|
2022-04-29 13:08:22 +00:00
|
|
|
cfg.NotificatorEnabled = v.GetBool(cfgEnableNATS)
|
2022-08-10 18:54:24 +00:00
|
|
|
cfg.TLSEnabled = v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile)
|
2022-08-11 22:48:56 +00:00
|
|
|
cfg.CopiesNumber = setCopiesNumber
|
2021-10-04 14:32:35 +00:00
|
|
|
|
2021-08-23 18:39:15 +00:00
|
|
|
return &cfg
|
|
|
|
}
|