mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
426c902b87
This PR changes the handling of bucket deletes for site replicated setups to hold on to deleted bucket state until it syncs to all the clusters participating in site replication.
384 lines
11 KiB
Go
384 lines
11 KiB
Go
// 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/>.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/url"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/minio/cli"
|
|
"github.com/minio/madmin-go"
|
|
"github.com/minio/minio/internal/config"
|
|
xhttp "github.com/minio/minio/internal/http"
|
|
"github.com/minio/minio/internal/logger"
|
|
"github.com/minio/pkg/certs"
|
|
"github.com/minio/pkg/env"
|
|
)
|
|
|
|
var gatewayCmd = cli.Command{
|
|
Name: "gateway",
|
|
Usage: "start object storage gateway",
|
|
Flags: append(ServerFlags, GlobalFlags...),
|
|
HideHelpCommand: true,
|
|
}
|
|
|
|
// GatewayLocker implements custom NewNSLock implementation
|
|
type GatewayLocker struct {
|
|
ObjectLayer
|
|
nsMutex *nsLockMap
|
|
}
|
|
|
|
// NewNSLock - implements gateway level locker
|
|
func (l *GatewayLocker) NewNSLock(bucket string, objects ...string) RWLocker {
|
|
return l.nsMutex.NewNSLock(nil, bucket, objects...)
|
|
}
|
|
|
|
// Walk - implements common gateway level Walker, to walk on all objects recursively at a prefix
|
|
func (l *GatewayLocker) Walk(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo, opts ObjectOptions) error {
|
|
walk := func(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo) error {
|
|
go func() {
|
|
// Make sure the results channel is ready to be read when we're done.
|
|
defer close(results)
|
|
|
|
var marker string
|
|
|
|
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
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
if err := l.ObjectLayer.Walk(ctx, bucket, prefix, results, opts); err != nil {
|
|
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)}
|
|
}
|
|
|
|
// RegisterGatewayCommand registers a new command for gateway.
|
|
func RegisterGatewayCommand(cmd cli.Command) error {
|
|
cmd.Flags = append(append(cmd.Flags, ServerFlags...), GlobalFlags...)
|
|
gatewayCmd.Subcommands = append(gatewayCmd.Subcommands, cmd)
|
|
return nil
|
|
}
|
|
|
|
// ParseGatewayEndpoint - Return endpoint.
|
|
func ParseGatewayEndpoint(arg string) (endPoint string, secure bool, err error) {
|
|
schemeSpecified := len(strings.Split(arg, "://")) > 1
|
|
if !schemeSpecified {
|
|
// Default connection will be "secure".
|
|
arg = "https://" + arg
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// ValidateGatewayArguments - Validate gateway arguments.
|
|
func ValidateGatewayArguments(serverAddr, endpointAddr string) error {
|
|
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 {
|
|
return fmt.Errorf("endpoint points to the local gateway")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StartGateway - handler for 'minio gateway <name>'.
|
|
func StartGateway(ctx *cli.Context, gw Gateway) {
|
|
signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
|
|
|
go handleSignals()
|
|
|
|
signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
|
// This is only to uniquely identify each gateway deployments.
|
|
globalDeploymentID = env.Get("MINIO_GATEWAY_DEPLOYMENT_ID", mustGetUUID())
|
|
xhttp.SetDeploymentID(globalDeploymentID)
|
|
|
|
if gw == nil {
|
|
logger.FatalIf(errUnexpected, "Gateway implementation not initialized")
|
|
}
|
|
|
|
// Validate if we have access, secret set through environment.
|
|
globalGatewayName = gw.Name()
|
|
gatewayName := gw.Name()
|
|
if ctx.Args().First() == "help" {
|
|
cli.ShowCommandHelpAndExit(ctx, gatewayName, 1)
|
|
}
|
|
|
|
// Initialize globalConsoleSys system
|
|
globalConsoleSys = NewConsoleLogger(GlobalContext)
|
|
logger.AddSystemTarget(globalConsoleSys)
|
|
|
|
// Handle common command args.
|
|
handleCommonCmdArgs(ctx)
|
|
|
|
// Check and load TLS certificates.
|
|
var err error
|
|
globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig()
|
|
logger.FatalIf(err, "Invalid TLS certificate file")
|
|
|
|
// Check and load Root CAs.
|
|
globalRootCAs, err = certs.GetRootCAs(globalCertsCADir.Get())
|
|
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
|
|
|
// Add the global public crts as part of global root CAs
|
|
for _, publicCrt := range globalPublicCerts {
|
|
globalRootCAs.AddCert(publicCrt)
|
|
}
|
|
|
|
// Register root CAs for remote ENVs
|
|
env.RegisterGlobalCAs(globalRootCAs)
|
|
|
|
// Initialize all help
|
|
initHelp()
|
|
|
|
// 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.
|
|
logger.FatalIf(checkPortAvailability(globalMinioHost, globalMinioPort), "Unable to start the gateway")
|
|
|
|
// Handle gateway specific env
|
|
gatewayHandleEnvVars()
|
|
|
|
// Initialize KMS configuration
|
|
handleKMSConfig()
|
|
|
|
// Set system resources to maximum.
|
|
setMaxResources()
|
|
|
|
// Set when gateway is enabled
|
|
globalIsGateway = true
|
|
|
|
// Initialize server config.
|
|
srvCfg := newServerConfig()
|
|
|
|
// Override any values from ENVs.
|
|
lookupConfigs(srvCfg, nil)
|
|
|
|
// hold the mutex lock before a new config is assigned.
|
|
globalServerConfigMu.Lock()
|
|
globalServerConfig = srvCfg
|
|
globalServerConfigMu.Unlock()
|
|
|
|
// 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()
|
|
|
|
// Enable STS router if etcd is enabled.
|
|
registerSTSRouter(router)
|
|
|
|
// Enable IAM admin APIs if etcd is enabled, if not just enable basic
|
|
// operations such as profiling, server info etc.
|
|
registerAdminRouter(router, false)
|
|
|
|
// Add healthcheck router
|
|
registerHealthCheckRouter(router)
|
|
|
|
// Add server metrics router
|
|
registerMetricsRouter(router)
|
|
|
|
// Add API router.
|
|
registerAPIRouter(router)
|
|
|
|
// Enable bucket forwarding handler only if bucket federation is enabled.
|
|
if globalDNSConfig != nil && globalBucketFederation {
|
|
globalHandlers = append(globalHandlers, setBucketForwardingHandler)
|
|
}
|
|
|
|
// Use all the middlewares
|
|
router.Use(globalHandlers...)
|
|
|
|
var getCert certs.GetCertificateFunc
|
|
if globalTLSCerts != nil {
|
|
getCert = globalTLSCerts.GetCertificate
|
|
}
|
|
|
|
listeners := ctx.Int("listeners")
|
|
if listeners == 0 {
|
|
listeners = 1
|
|
}
|
|
addrs := make([]string, 0, listeners)
|
|
for i := 0; i < listeners; i++ {
|
|
addrs = append(addrs, globalMinioAddr)
|
|
}
|
|
|
|
httpServer := xhttp.NewServer(addrs).
|
|
UseHandler(setCriticalErrorHandler(corsHandler(router))).
|
|
UseTLSConfig(newTLSConfig(getCert)).
|
|
UseShutdownTimeout(ctx.Duration("shutdown-timeout")).
|
|
UseBaseContext(GlobalContext).
|
|
UseCustomLogger(log.New(ioutil.Discard, "", 0)) // Turn-off random logging by Go stdlib
|
|
|
|
go func() {
|
|
globalHTTPServerErrorCh <- httpServer.Start(GlobalContext)
|
|
}()
|
|
|
|
setHTTPServer(httpServer)
|
|
|
|
newObject, err := gw.NewGatewayLayer(madmin.Credentials{
|
|
AccessKey: globalActiveCred.AccessKey,
|
|
SecretKey: globalActiveCred.SecretKey,
|
|
})
|
|
if err != nil {
|
|
if errors.Is(err, errFreshDisk) {
|
|
err = config.ErrInvalidFSValue(err)
|
|
}
|
|
logger.FatalIf(err, "Unable to initialize gateway backend")
|
|
}
|
|
newObject = NewGatewayLayerWithLocker(newObject)
|
|
|
|
// Calls all New() for all sub-systems.
|
|
initAllSubsystems()
|
|
|
|
// Once endpoints are finalized, initialize the new object api in safe mode.
|
|
globalObjLayerMutex.Lock()
|
|
globalObjectAPI = newObject
|
|
globalObjLayerMutex.Unlock()
|
|
|
|
go globalIAMSys.Init(GlobalContext, newObject, globalEtcdClient, globalRefreshIAMInterval)
|
|
|
|
if gatewayName == NASBackendGateway {
|
|
buckets, err := newObject.ListBuckets(GlobalContext, BucketOptions{})
|
|
if err != nil {
|
|
logger.Fatal(err, "Unable to list buckets")
|
|
}
|
|
logger.FatalIf(globalBucketMetadataSys.Init(GlobalContext, buckets, newObject), "Unable to initialize bucket metadata")
|
|
|
|
logger.FatalIf(globalNotificationSys.InitBucketTargets(GlobalContext, newObject), "Unable to initialize bucket targets for notification system")
|
|
}
|
|
|
|
if globalCacheConfig.Enabled {
|
|
// initialize the new disk cache objects.
|
|
var cacheAPI CacheObjectLayer
|
|
cacheAPI, err = newServerCacheObjects(GlobalContext, globalCacheConfig)
|
|
logger.FatalIf(err, "Unable to initialize disk caching")
|
|
|
|
globalObjLayerMutex.Lock()
|
|
globalCacheObjectAPI = cacheAPI
|
|
globalObjLayerMutex.Unlock()
|
|
}
|
|
|
|
// Populate existing buckets to the etcd backend
|
|
if globalDNSConfig != nil {
|
|
buckets, err := newObject.ListBuckets(GlobalContext, BucketOptions{})
|
|
if err != nil {
|
|
logger.Fatal(err, "Unable to list buckets")
|
|
}
|
|
initFederatorBackend(buckets, newObject)
|
|
}
|
|
|
|
// Verify if object layer supports
|
|
// - encryption
|
|
// - compression
|
|
verifyObjectLayerFeatures("gateway "+gatewayName, newObject)
|
|
|
|
// Check for updates in non-blocking manner.
|
|
go func() {
|
|
if !globalCLIContext.Quiet && !globalInplaceUpdateDisabled {
|
|
// Check for new updates from dl.min.io.
|
|
checkUpdate(getMinioMode())
|
|
}
|
|
}()
|
|
|
|
if !globalCLIContext.Quiet {
|
|
// Print gateway startup message.
|
|
printGatewayStartupMessage(getAPIEndpoints(), gatewayName)
|
|
}
|
|
|
|
if globalBrowserEnabled {
|
|
srv, err := initConsoleServer()
|
|
if err != nil {
|
|
logger.FatalIf(err, "Unable to initialize console service")
|
|
}
|
|
|
|
setConsoleSrv(srv)
|
|
|
|
go func() {
|
|
logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server")
|
|
}()
|
|
}
|
|
|
|
if serverDebugLog {
|
|
logger.Info("== DEBUG Mode enabled ==")
|
|
logger.Info("Currently set environment settings:")
|
|
for _, v := range os.Environ() {
|
|
logger.Info(v)
|
|
}
|
|
logger.Info("======")
|
|
}
|
|
|
|
<-globalOSSignalCh
|
|
}
|