2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-03-16 15:21:58 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2020-04-16 14:54:12 -04:00
|
|
|
"context"
|
2022-05-30 13:58:37 -04:00
|
|
|
"errors"
|
2017-03-16 15:21:58 -04:00
|
|
|
"fmt"
|
2022-09-19 14:05:16 -04:00
|
|
|
"io"
|
2021-11-29 12:06:56 -05:00
|
|
|
"log"
|
2017-04-11 20:44:26 -04:00
|
|
|
"net/url"
|
2017-07-12 19:33:21 -04:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2017-04-11 20:44:26 -04:00
|
|
|
"strings"
|
2017-07-12 19:33:21 -04:00
|
|
|
"syscall"
|
2017-06-05 18:18:03 -04:00
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/minio/cli"
|
2021-06-14 15:53:49 -04:00
|
|
|
"github.com/minio/madmin-go"
|
2022-05-30 13:58:37 -04:00
|
|
|
"github.com/minio/minio/internal/config"
|
2021-06-01 17:59:40 -04:00
|
|
|
xhttp "github.com/minio/minio/internal/http"
|
|
|
|
"github.com/minio/minio/internal/logger"
|
2021-05-28 18:17:01 -04:00
|
|
|
"github.com/minio/pkg/certs"
|
|
|
|
"github.com/minio/pkg/env"
|
2017-03-16 15:21:58 -04:00
|
|
|
)
|
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
var gatewayCmd = cli.Command{
|
|
|
|
Name: "gateway",
|
|
|
|
Usage: "start object storage gateway",
|
|
|
|
Flags: append(ServerFlags, GlobalFlags...),
|
|
|
|
HideHelpCommand: true,
|
|
|
|
}
|
2017-06-09 02:28:45 -04:00
|
|
|
|
2020-06-29 20:07:23 -04:00
|
|
|
// GatewayLocker implements custom NewNSLock implementation
|
|
|
|
type GatewayLocker struct {
|
|
|
|
ObjectLayer
|
|
|
|
nsMutex *nsLockMap
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNSLock - implements gateway level locker
|
2020-11-04 11:25:42 -05:00
|
|
|
func (l *GatewayLocker) NewNSLock(bucket string, objects ...string) RWLocker {
|
|
|
|
return l.nsMutex.NewNSLock(nil, bucket, objects...)
|
2020-06-29 20:07:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Walk - implements common gateway level Walker, to walk on all objects recursively at a prefix
|
2020-07-11 01:21:04 -04:00
|
|
|
func (l *GatewayLocker) Walk(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo, opts ObjectOptions) error {
|
2020-06-29 20:07:23 -04:00
|
|
|
walk := func(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo) error {
|
2020-07-27 01:56:05 -04:00
|
|
|
go func() {
|
|
|
|
// Make sure the results channel is ready to be read when we're done.
|
|
|
|
defer close(results)
|
2020-07-01 17:29:45 -04:00
|
|
|
|
2020-07-27 01:56:05 -04:00
|
|
|
var marker string
|
2020-07-01 17:29:45 -04:00
|
|
|
|
2020-07-27 01:56:05 -04:00
|
|
|
for {
|
|
|
|
// set maxKeys to '0' to list maximum possible objects in single call.
|
|
|
|
loi, err := l.ObjectLayer.ListObjects(ctx, bucket, prefix, marker, "", 0)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
marker = loi.NextMarker
|
|
|
|
for _, obj := range loi.Objects {
|
|
|
|
select {
|
|
|
|
case results <- obj:
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !loi.IsTruncated {
|
|
|
|
break
|
2020-06-29 20:07:23 -04:00
|
|
|
}
|
|
|
|
}
|
2020-07-27 01:56:05 -04:00
|
|
|
}()
|
2020-06-29 20:07:23 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-11 01:21:04 -04:00
|
|
|
if err := l.ObjectLayer.Walk(ctx, bucket, prefix, results, opts); err != nil {
|
2020-06-29 20:07:23 -04:00
|
|
|
if _, ok := err.(NotImplemented); ok {
|
|
|
|
return walk(ctx, bucket, prefix, results)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewGatewayLayerWithLocker - initialize gateway with locker.
|
|
|
|
func NewGatewayLayerWithLocker(gwLayer ObjectLayer) ObjectLayer {
|
|
|
|
return &GatewayLocker{ObjectLayer: gwLayer, nsMutex: newNSLock(false)}
|
|
|
|
}
|
|
|
|
|
2017-10-27 18:07:46 -04:00
|
|
|
// RegisterGatewayCommand registers a new command for gateway.
|
|
|
|
func RegisterGatewayCommand(cmd cli.Command) error {
|
2019-07-03 17:31:19 -04:00
|
|
|
cmd.Flags = append(append(cmd.Flags, ServerFlags...), GlobalFlags...)
|
2017-10-27 18:07:46 -04:00
|
|
|
gatewayCmd.Subcommands = append(gatewayCmd.Subcommands, cmd)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-05 20:58:09 -05:00
|
|
|
// ParseGatewayEndpoint - Return endpoint.
|
|
|
|
func ParseGatewayEndpoint(arg string) (endPoint string, secure bool, err error) {
|
2017-04-11 20:44:26 -04:00
|
|
|
schemeSpecified := len(strings.Split(arg, "://")) > 1
|
|
|
|
if !schemeSpecified {
|
|
|
|
// Default connection will be "secure".
|
|
|
|
arg = "https://" + arg
|
|
|
|
}
|
2017-04-27 14:26:00 -04:00
|
|
|
|
2017-04-11 20:44:26 -04:00
|
|
|
u, err := url.Parse(arg)
|
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch u.Scheme {
|
|
|
|
case "http":
|
|
|
|
return u.Host, false, nil
|
|
|
|
case "https":
|
|
|
|
return u.Host, true, nil
|
|
|
|
default:
|
|
|
|
return "", false, fmt.Errorf("Unrecognized scheme %s", u.Scheme)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-05 20:58:09 -05:00
|
|
|
// ValidateGatewayArguments - Validate gateway arguments.
|
|
|
|
func ValidateGatewayArguments(serverAddr, endpointAddr string) error {
|
2017-06-08 14:20:56 -04:00
|
|
|
if err := CheckLocalServerAddr(serverAddr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if endpointAddr != "" {
|
|
|
|
// Reject the endpoint if it points to the gateway handler itself.
|
|
|
|
sameTarget, err := sameLocalAddrs(endpointAddr, serverAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if sameTarget {
|
2017-11-25 14:58:29 -05:00
|
|
|
return fmt.Errorf("endpoint points to the local gateway")
|
2017-06-08 14:20:56 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-05 20:58:09 -05:00
|
|
|
// StartGateway - handler for 'minio gateway <name>'.
|
|
|
|
func StartGateway(ctx *cli.Context, gw Gateway) {
|
2021-06-17 23:27:04 -04:00
|
|
|
signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
|
|
|
|
|
|
|
go handleSignals()
|
|
|
|
|
|
|
|
signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
2020-05-18 14:35:05 -04:00
|
|
|
// This is only to uniquely identify each gateway deployments.
|
|
|
|
globalDeploymentID = env.Get("MINIO_GATEWAY_DEPLOYMENT_ID", mustGetUUID())
|
2022-02-23 16:36:01 -05:00
|
|
|
xhttp.SetDeploymentID(globalDeploymentID)
|
2020-05-18 14:35:05 -04:00
|
|
|
|
2017-12-05 20:58:09 -05:00
|
|
|
if gw == nil {
|
2018-05-09 18:11:24 -04:00
|
|
|
logger.FatalIf(errUnexpected, "Gateway implementation not initialized")
|
2017-12-05 20:58:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validate if we have access, secret set through environment.
|
2019-08-14 14:43:43 -04:00
|
|
|
globalGatewayName = gw.Name()
|
2017-12-05 20:58:09 -05:00
|
|
|
gatewayName := gw.Name()
|
|
|
|
if ctx.Args().First() == "help" {
|
|
|
|
cli.ShowCommandHelpAndExit(ctx, gatewayName, 1)
|
|
|
|
}
|
|
|
|
|
2020-09-15 21:02:54 -04:00
|
|
|
// Initialize globalConsoleSys system
|
|
|
|
globalConsoleSys = NewConsoleLogger(GlobalContext)
|
2022-02-24 12:05:33 -05:00
|
|
|
logger.AddSystemTarget(globalConsoleSys)
|
2020-09-15 21:02:54 -04:00
|
|
|
|
2017-06-09 22:50:51 -04:00
|
|
|
// Handle common command args.
|
|
|
|
handleCommonCmdArgs(ctx)
|
|
|
|
|
2020-08-06 21:03:16 -04:00
|
|
|
// Check and load TLS certificates.
|
|
|
|
var err error
|
2020-12-22 00:42:38 -05:00
|
|
|
globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig()
|
2020-08-06 21:03:16 -04:00
|
|
|
logger.FatalIf(err, "Invalid TLS certificate file")
|
|
|
|
|
|
|
|
// Check and load Root CAs.
|
2020-08-11 11:29:50 -04:00
|
|
|
globalRootCAs, err = certs.GetRootCAs(globalCertsCADir.Get())
|
2020-08-06 21:03:16 -04:00
|
|
|
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
|
|
|
|
2020-08-13 12:46:50 -04:00
|
|
|
// Add the global public crts as part of global root CAs
|
|
|
|
for _, publicCrt := range globalPublicCerts {
|
|
|
|
globalRootCAs.AddCert(publicCrt)
|
|
|
|
}
|
|
|
|
|
2020-08-06 21:03:16 -04:00
|
|
|
// Register root CAs for remote ENVs
|
|
|
|
env.RegisterGlobalCAs(globalRootCAs)
|
|
|
|
|
2019-12-04 18:32:37 -05:00
|
|
|
// Initialize all help
|
|
|
|
initHelp()
|
|
|
|
|
2018-08-18 00:06:36 -04:00
|
|
|
// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
|
|
|
|
// to IPv6 address ie minio will start listening on IPv6 address whereas another
|
|
|
|
// (non-)minio process is listening on IPv4 of given port.
|
|
|
|
// To avoid this error situation we check for port availability.
|
2019-06-24 18:02:40 -04:00
|
|
|
logger.FatalIf(checkPortAvailability(globalMinioHost, globalMinioPort), "Unable to start the gateway")
|
2018-08-18 00:06:36 -04:00
|
|
|
|
2019-01-05 17:16:43 -05:00
|
|
|
// Handle gateway specific env
|
2019-11-01 18:53:16 -04:00
|
|
|
gatewayHandleEnvVars()
|
2019-01-05 17:16:43 -05:00
|
|
|
|
2022-06-13 16:06:13 -04:00
|
|
|
// Initialize KMS configuration
|
|
|
|
handleKMSConfig()
|
|
|
|
|
2017-12-24 09:39:30 -05:00
|
|
|
// Set system resources to maximum.
|
2020-06-16 21:57:29 -04:00
|
|
|
setMaxResources()
|
2017-12-24 09:39:30 -05:00
|
|
|
|
2019-06-07 18:49:13 -04:00
|
|
|
// Set when gateway is enabled
|
|
|
|
globalIsGateway = true
|
|
|
|
|
2020-02-11 22:38:02 -05:00
|
|
|
// Initialize router. `SkipClean(true)` stops gorilla/mux from
|
|
|
|
// normalizing URL path minio/minio#3256
|
|
|
|
// avoid URL path encoding minio/minio#8950
|
|
|
|
router := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
2017-06-01 12:43:20 -04:00
|
|
|
|
2021-07-10 11:32:52 -04:00
|
|
|
// Enable STS router if etcd is enabled.
|
|
|
|
registerSTSRouter(router)
|
2019-01-23 14:10:59 -05:00
|
|
|
|
2018-12-18 16:03:26 -05:00
|
|
|
// Enable IAM admin APIs if etcd is enabled, if not just enable basic
|
|
|
|
// operations such as profiling, server info etc.
|
2021-09-01 14:34:07 -04:00
|
|
|
registerAdminRouter(router, false)
|
2018-12-18 16:03:26 -05:00
|
|
|
|
2018-04-04 22:07:54 -04:00
|
|
|
// Add healthcheck router
|
|
|
|
registerHealthCheckRouter(router)
|
|
|
|
|
2018-05-30 00:43:46 -04:00
|
|
|
// Add server metrics router
|
|
|
|
registerMetricsRouter(router)
|
|
|
|
|
2018-04-04 22:07:54 -04:00
|
|
|
// Add API router.
|
2020-09-15 16:57:15 -04:00
|
|
|
registerAPIRouter(router)
|
2017-03-16 15:21:58 -04:00
|
|
|
|
2020-06-11 11:19:55 -04:00
|
|
|
// Use all the middlewares
|
2021-01-04 12:54:22 -05:00
|
|
|
router.Use(globalHandlers...)
|
2020-06-11 11:19:55 -04:00
|
|
|
|
2018-05-31 15:30:15 -04:00
|
|
|
var getCert certs.GetCertificateFunc
|
|
|
|
if globalTLSCerts != nil {
|
|
|
|
getCert = globalTLSCerts.GetCertificate
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:58:24 -04:00
|
|
|
listeners := ctx.Int("listeners")
|
|
|
|
if listeners == 0 {
|
|
|
|
listeners = 1
|
|
|
|
}
|
|
|
|
addrs := make([]string, 0, listeners)
|
|
|
|
for i := 0; i < listeners; i++ {
|
|
|
|
addrs = append(addrs, globalMinioAddr)
|
|
|
|
}
|
|
|
|
|
2021-11-29 12:06:56 -05:00
|
|
|
httpServer := xhttp.NewServer(addrs).
|
|
|
|
UseHandler(setCriticalErrorHandler(corsHandler(router))).
|
|
|
|
UseTLSConfig(newTLSConfig(getCert)).
|
|
|
|
UseShutdownTimeout(ctx.Duration("shutdown-timeout")).
|
|
|
|
UseBaseContext(GlobalContext).
|
2022-09-19 14:05:16 -04:00
|
|
|
UseCustomLogger(log.New(io.Discard, "", 0)) // Turn-off random logging by Go stdlib
|
2021-11-29 12:06:56 -05:00
|
|
|
|
2017-03-16 15:21:58 -04:00
|
|
|
go func() {
|
2021-10-05 13:13:04 -04:00
|
|
|
globalHTTPServerErrorCh <- httpServer.Start(GlobalContext)
|
2017-03-16 15:21:58 -04:00
|
|
|
}()
|
|
|
|
|
2021-11-18 20:19:58 -05:00
|
|
|
setHTTPServer(httpServer)
|
2019-12-02 18:54:26 -05:00
|
|
|
|
2021-06-14 15:53:49 -04:00
|
|
|
newObject, err := gw.NewGatewayLayer(madmin.Credentials{
|
|
|
|
AccessKey: globalActiveCred.AccessKey,
|
|
|
|
SecretKey: globalActiveCred.SecretKey,
|
|
|
|
})
|
2018-12-18 13:42:09 -05:00
|
|
|
if err != nil {
|
2022-05-30 13:58:37 -04:00
|
|
|
if errors.Is(err, errFreshDisk) {
|
|
|
|
err = config.ErrInvalidFSValue(err)
|
|
|
|
}
|
2018-12-18 13:42:09 -05:00
|
|
|
logger.FatalIf(err, "Unable to initialize gateway backend")
|
|
|
|
}
|
2019-11-19 19:45:35 -05:00
|
|
|
newObject = NewGatewayLayerWithLocker(newObject)
|
|
|
|
|
2020-10-19 12:54:40 -04:00
|
|
|
// Calls all New() for all sub-systems.
|
2022-10-14 06:08:40 -04:00
|
|
|
initAllSubsystems(GlobalContext)
|
2020-10-19 12:54:40 -04:00
|
|
|
|
2019-11-09 12:27:23 -05:00
|
|
|
// Once endpoints are finalized, initialize the new object api in safe mode.
|
|
|
|
globalObjLayerMutex.Lock()
|
|
|
|
globalObjectAPI = newObject
|
|
|
|
globalObjLayerMutex.Unlock()
|
|
|
|
|
2022-09-26 12:04:54 -04:00
|
|
|
// Initialize server config.
|
|
|
|
srvCfg := newServerConfig()
|
|
|
|
|
|
|
|
// Override any values from ENVs.
|
|
|
|
lookupConfigs(srvCfg, newObject)
|
|
|
|
|
|
|
|
// hold the mutex lock before a new config is assigned.
|
|
|
|
globalServerConfigMu.Lock()
|
|
|
|
globalServerConfig = srvCfg
|
|
|
|
globalServerConfigMu.Unlock()
|
|
|
|
|
2022-02-07 13:39:57 -05:00
|
|
|
go globalIAMSys.Init(GlobalContext, newObject, globalEtcdClient, globalRefreshIAMInterval)
|
|
|
|
|
2020-08-26 11:52:46 -04:00
|
|
|
if gatewayName == NASBackendGateway {
|
2022-07-25 20:51:32 -04:00
|
|
|
buckets, err := newObject.ListBuckets(GlobalContext, BucketOptions{})
|
2020-02-02 04:52:07 -05:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatal(err, "Unable to list buckets")
|
|
|
|
}
|
2022-02-07 13:39:57 -05:00
|
|
|
logger.FatalIf(globalBucketMetadataSys.Init(GlobalContext, buckets, newObject), "Unable to initialize bucket metadata")
|
2018-10-12 15:25:59 -04:00
|
|
|
|
2022-08-24 09:42:36 -04:00
|
|
|
logger.FatalIf(globalEventNotifier.InitBucketTargets(GlobalContext, newObject), "Unable to initialize bucket targets for notification system")
|
2022-02-07 13:39:57 -05:00
|
|
|
}
|
2018-10-09 17:00:01 -04:00
|
|
|
|
2019-11-09 12:27:23 -05:00
|
|
|
if globalCacheConfig.Enabled {
|
|
|
|
// initialize the new disk cache objects.
|
|
|
|
var cacheAPI CacheObjectLayer
|
2020-03-22 15:16:36 -04:00
|
|
|
cacheAPI, err = newServerCacheObjects(GlobalContext, globalCacheConfig)
|
2022-08-04 19:10:08 -04:00
|
|
|
logger.FatalIf(err, "Unable to initialize drive caching")
|
2019-07-19 16:20:33 -04:00
|
|
|
|
2019-11-09 12:27:23 -05:00
|
|
|
globalObjLayerMutex.Lock()
|
|
|
|
globalCacheObjectAPI = cacheAPI
|
|
|
|
globalObjLayerMutex.Unlock()
|
2019-10-31 02:39:09 -04:00
|
|
|
}
|
2018-12-14 16:35:48 -05:00
|
|
|
|
2019-12-17 16:50:07 -05:00
|
|
|
// Populate existing buckets to the etcd backend
|
|
|
|
if globalDNSConfig != nil {
|
2022-07-25 20:51:32 -04:00
|
|
|
buckets, err := newObject.ListBuckets(GlobalContext, BucketOptions{})
|
2019-12-17 16:50:07 -05:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatal(err, "Unable to list buckets")
|
|
|
|
}
|
|
|
|
initFederatorBackend(buckets, newObject)
|
|
|
|
}
|
|
|
|
|
2019-07-26 05:41:16 -04:00
|
|
|
// Verify if object layer supports
|
|
|
|
// - encryption
|
|
|
|
// - compression
|
|
|
|
verifyObjectLayerFeatures("gateway "+gatewayName, newObject)
|
2018-12-14 16:35:48 -05:00
|
|
|
|
2021-12-13 12:43:03 -05:00
|
|
|
// Check for updates in non-blocking manner.
|
|
|
|
go func() {
|
|
|
|
if !globalCLIContext.Quiet && !globalInplaceUpdateDisabled {
|
|
|
|
// Check for new updates from dl.min.io.
|
|
|
|
checkUpdate(getMinioMode())
|
|
|
|
}
|
|
|
|
}()
|
2017-12-05 20:58:09 -05:00
|
|
|
|
2021-06-14 15:53:49 -04:00
|
|
|
if !globalCLIContext.Quiet {
|
2017-06-09 22:50:51 -04:00
|
|
|
// Print gateway startup message.
|
2018-12-14 02:37:46 -05:00
|
|
|
printGatewayStartupMessage(getAPIEndpoints(), gatewayName)
|
2017-03-16 15:21:58 -04:00
|
|
|
}
|
|
|
|
|
2021-06-17 23:27:04 -04:00
|
|
|
if globalBrowserEnabled {
|
2021-11-18 20:19:58 -05:00
|
|
|
srv, err := initConsoleServer()
|
2021-06-17 23:27:04 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.FatalIf(err, "Unable to initialize console service")
|
|
|
|
}
|
|
|
|
|
2021-11-18 20:19:58 -05:00
|
|
|
setConsoleSrv(srv)
|
|
|
|
|
2021-06-17 23:27:04 -04:00
|
|
|
go func() {
|
2021-11-18 20:19:58 -05:00
|
|
|
logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server")
|
2021-06-17 23:27:04 -04:00
|
|
|
}()
|
|
|
|
}
|
2021-11-18 20:19:58 -05:00
|
|
|
|
|
|
|
if serverDebugLog {
|
|
|
|
logger.Info("== DEBUG Mode enabled ==")
|
|
|
|
logger.Info("Currently set environment settings:")
|
|
|
|
for _, v := range os.Environ() {
|
|
|
|
logger.Info(v)
|
|
|
|
}
|
|
|
|
logger.Info("======")
|
|
|
|
}
|
|
|
|
|
2021-08-31 21:52:48 -04:00
|
|
|
<-globalOSSignalCh
|
2017-03-16 15:21:58 -04:00
|
|
|
}
|