storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/gateway-main.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2017-2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net"
    23  	"net/url"
    24  	"os"
    25  	"os/signal"
    26  	"strings"
    27  	"syscall"
    28  
    29  	"github.com/gorilla/mux"
    30  	"github.com/minio/cli"
    31  
    32  	xhttp "storj.io/minio/cmd/http"
    33  	"storj.io/minio/cmd/logger"
    34  	"storj.io/minio/pkg/certs"
    35  	"storj.io/minio/pkg/color"
    36  	"storj.io/minio/pkg/env"
    37  )
    38  
    39  var (
    40  	gatewayCmd = cli.Command{
    41  		Name:            "gateway",
    42  		Usage:           "start object storage gateway",
    43  		Flags:           append(ServerFlags, GlobalFlags...),
    44  		HideHelpCommand: true,
    45  	}
    46  )
    47  
    48  // GatewayLocker implements custom NewNSLock implementation
    49  type GatewayLocker struct {
    50  	ObjectLayer
    51  	nsMutex *nsLockMap
    52  }
    53  
    54  // NewNSLock - implements gateway level locker
    55  func (l *GatewayLocker) NewNSLock(bucket string, objects ...string) RWLocker {
    56  	return l.nsMutex.NewNSLock(nil, bucket, objects...)
    57  }
    58  
    59  // Walk - implements common gateway level Walker, to walk on all objects recursively at a prefix
    60  func (l *GatewayLocker) Walk(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo, opts ObjectOptions) error {
    61  	walk := func(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo) error {
    62  		go func() {
    63  			// Make sure the results channel is ready to be read when we're done.
    64  			defer close(results)
    65  
    66  			var marker string
    67  
    68  			for {
    69  				// set maxKeys to '0' to list maximum possible objects in single call.
    70  				loi, err := l.ObjectLayer.ListObjects(ctx, bucket, prefix, marker, "", 0)
    71  				if err != nil {
    72  					logger.LogIf(ctx, err)
    73  					return
    74  				}
    75  				marker = loi.NextMarker
    76  				for _, obj := range loi.Objects {
    77  					select {
    78  					case results <- obj:
    79  					case <-ctx.Done():
    80  						return
    81  					}
    82  				}
    83  				if !loi.IsTruncated {
    84  					break
    85  				}
    86  			}
    87  		}()
    88  		return nil
    89  	}
    90  
    91  	if err := l.ObjectLayer.Walk(ctx, bucket, prefix, results, opts); err != nil {
    92  		if _, ok := err.(NotImplemented); ok {
    93  			return walk(ctx, bucket, prefix, results)
    94  		}
    95  		return err
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  // NewGatewayLayerWithLocker - initialize gateway with locker.
   102  func NewGatewayLayerWithLocker(gwLayer ObjectLayer) ObjectLayer {
   103  	return &GatewayLocker{ObjectLayer: gwLayer, nsMutex: newNSLock(false)}
   104  }
   105  
   106  // RegisterGatewayCommand registers a new command for gateway.
   107  func RegisterGatewayCommand(cmd cli.Command) error {
   108  	cmd.Flags = append(append(cmd.Flags, ServerFlags...), GlobalFlags...)
   109  	gatewayCmd.Subcommands = append(gatewayCmd.Subcommands, cmd)
   110  	return nil
   111  }
   112  
   113  // ParseGatewayEndpoint - Return endpoint.
   114  func ParseGatewayEndpoint(arg string) (endPoint string, secure bool, err error) {
   115  	schemeSpecified := len(strings.Split(arg, "://")) > 1
   116  	if !schemeSpecified {
   117  		// Default connection will be "secure".
   118  		arg = "https://" + arg
   119  	}
   120  
   121  	u, err := url.Parse(arg)
   122  	if err != nil {
   123  		return "", false, err
   124  	}
   125  
   126  	switch u.Scheme {
   127  	case "http":
   128  		return u.Host, false, nil
   129  	case "https":
   130  		return u.Host, true, nil
   131  	default:
   132  		return "", false, fmt.Errorf("Unrecognized scheme %s", u.Scheme)
   133  	}
   134  }
   135  
   136  // ValidateGatewayArguments - Validate gateway arguments.
   137  func ValidateGatewayArguments(serverAddr, endpointAddr string) error {
   138  	if err := CheckLocalServerAddr(serverAddr); err != nil {
   139  		return err
   140  	}
   141  
   142  	if endpointAddr != "" {
   143  		// Reject the endpoint if it points to the gateway handler itself.
   144  		sameTarget, err := sameLocalAddrs(endpointAddr, serverAddr)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		if sameTarget {
   149  			return fmt.Errorf("endpoint points to the local gateway")
   150  		}
   151  	}
   152  	return nil
   153  }
   154  
   155  // StartGateway - handler for 'minio gateway <name>'.
   156  func StartGateway(ctx *cli.Context, gw Gateway) {
   157  	defer globalDNSCache.Stop()
   158  
   159  	// This is only to uniquely identify each gateway deployments.
   160  	globalDeploymentID = env.Get("MINIO_GATEWAY_DEPLOYMENT_ID", mustGetUUID())
   161  	logger.SetDeploymentID(globalDeploymentID)
   162  
   163  	if gw == nil {
   164  		logger.FatalIf(errUnexpected, "Gateway implementation not initialized")
   165  	}
   166  
   167  	// Validate if we have access, secret set through environment.
   168  	globalGatewayName = gw.Name()
   169  	gatewayName := gw.Name()
   170  	if ctx.Args().First() == "help" {
   171  		cli.ShowCommandHelpAndExit(ctx, gatewayName, 1)
   172  	}
   173  
   174  	// Initialize globalConsoleSys system
   175  	globalConsoleSys = NewConsoleLogger(GlobalContext)
   176  	logger.AddTarget(globalConsoleSys)
   177  
   178  	// Handle common command args.
   179  	handleCommonCmdArgs(ctx)
   180  
   181  	// Check and load TLS certificates.
   182  	var err error
   183  	globalPublicCerts, globalTLSCerts, GlobalIsTLS, err = getTLSConfig()
   184  	logger.FatalIf(err, "Invalid TLS certificate file")
   185  
   186  	// Check and load Root CAs.
   187  	globalRootCAs, err = certs.GetRootCAs(globalCertsCADir.Get())
   188  	logger.FatalIf(err, "Failed to read root CAs (%v)", err)
   189  
   190  	// Add the global public crts as part of global root CAs
   191  	for _, publicCrt := range globalPublicCerts {
   192  		globalRootCAs.AddCert(publicCrt)
   193  	}
   194  
   195  	// Register root CAs for remote ENVs
   196  	env.RegisterGlobalCAs(globalRootCAs)
   197  
   198  	// Initialize all help
   199  	initHelp()
   200  
   201  	// Get port to listen on from gateway address
   202  	globalMinioHost, globalMinioPort = mustSplitHostPort(GlobalCLIContext.Addr)
   203  
   204  	// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
   205  	// to IPv6 address ie minio will start listening on IPv6 address whereas another
   206  	// (non-)minio process is listening on IPv4 of given port.
   207  	// To avoid this error situation we check for port availability.
   208  	logger.FatalIf(checkPortAvailability(globalMinioHost, globalMinioPort), "Unable to start the gateway")
   209  
   210  	globalMinioEndpoint = func() string {
   211  		host := globalMinioHost
   212  		if host == "" {
   213  			host = sortIPs(localIP4.ToSlice())[0]
   214  		}
   215  		return fmt.Sprintf("%s://%s", getURLScheme(GlobalIsTLS), net.JoinHostPort(host, globalMinioPort))
   216  	}()
   217  
   218  	// Handle gateway specific env
   219  	gatewayHandleEnvVars()
   220  
   221  	// Set system resources to maximum.
   222  	setMaxResources()
   223  
   224  	// Set when gateway is enabled
   225  	GlobalIsGateway = true
   226  
   227  	enableConfigOps := false
   228  
   229  	// TODO: We need to move this code with globalConfigSys.Init()
   230  	// for now keep it here such that "s3" gateway layer initializes
   231  	// itself properly when KMS is set.
   232  
   233  	// Initialize server config.
   234  	srvCfg := newServerConfig()
   235  
   236  	// Override any values from ENVs.
   237  	lookupConfigs(srvCfg, nil)
   238  
   239  	// hold the mutex lock before a new config is assigned.
   240  	globalServerConfigMu.Lock()
   241  	globalServerConfig = srvCfg
   242  	globalServerConfigMu.Unlock()
   243  
   244  	// Initialize router. `SkipClean(true)` stops gorilla/mux from
   245  	// normalizing URL path minio/minio#3256
   246  	// avoid URL path encoding minio/minio#8950
   247  	router := mux.NewRouter().SkipClean(true).UseEncodedPath()
   248  
   249  	// Enable IAM admin APIs if etcd is enabled, if not just enable basic
   250  	// operations such as profiling, server info etc.
   251  	registerAdminRouter(router, enableConfigOps, false)
   252  
   253  	// Add healthcheck router
   254  	registerHealthCheckRouter(router)
   255  
   256  	// Add server metrics router
   257  	registerMetricsRouter(router)
   258  
   259  	// Register web router when its enabled.
   260  	if globalBrowserEnabled {
   261  		logger.FatalIf(registerWebRouter(router), "Unable to configure web browser")
   262  	}
   263  
   264  	// Add API router.
   265  	registerAPIRouter(router)
   266  
   267  	// Use all the middlewares
   268  	router.Use(GlobalHandlers...)
   269  
   270  	var getCert certs.GetCertificateFunc
   271  	if globalTLSCerts != nil {
   272  		getCert = globalTLSCerts.GetCertificate
   273  	}
   274  
   275  	httpServer := xhttp.NewServer([]string{GlobalCLIContext.Addr},
   276  		criticalErrorHandler{corsHandler(router)}, getCert)
   277  	httpServer.BaseContext = func(listener net.Listener) context.Context {
   278  		return GlobalContext
   279  	}
   280  	go func() {
   281  		globalHTTPServerErrorCh <- httpServer.Start()
   282  	}()
   283  
   284  	globalObjLayerMutex.Lock()
   285  	globalHTTPServer = httpServer
   286  	globalObjLayerMutex.Unlock()
   287  
   288  	signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
   289  
   290  	newObject, err := gw.NewGatewayLayer(globalActiveCred)
   291  	if err != nil {
   292  		globalHTTPServer.Shutdown()
   293  		logger.FatalIf(err, "Unable to initialize gateway backend")
   294  	}
   295  	newObject = NewGatewayLayerWithLocker(newObject)
   296  
   297  	// Calls all New() for all sub-systems.
   298  	newAllSubsystems()
   299  
   300  	// Once endpoints are finalized, initialize the new object api in safe mode.
   301  	globalObjLayerMutex.Lock()
   302  	globalObjectAPI = newObject
   303  	globalObjLayerMutex.Unlock()
   304  
   305  	if gatewayName == NASBackendGateway {
   306  		buckets, err := newObject.ListBuckets(GlobalContext)
   307  		if err != nil {
   308  			logger.Fatal(err, "Unable to list buckets")
   309  		}
   310  		logger.FatalIf(GlobalNotificationSys.Init(GlobalContext, buckets, newObject), "Unable to initialize notification system")
   311  	}
   312  
   313  	if globalCacheConfig.Enabled {
   314  		// initialize the new disk cache objects.
   315  		var cacheAPI CacheObjectLayer
   316  		cacheAPI, err = newServerCacheObjects(GlobalContext, globalCacheConfig)
   317  		logger.FatalIf(err, "Unable to initialize disk caching")
   318  
   319  		globalObjLayerMutex.Lock()
   320  		globalCacheObjectAPI = cacheAPI
   321  		globalObjLayerMutex.Unlock()
   322  	}
   323  
   324  	// Verify if object layer supports
   325  	// - encryption
   326  	// - compression
   327  	verifyObjectLayerFeatures("gateway "+gatewayName, newObject)
   328  
   329  	// Prints the formatted startup message once object layer is initialized.
   330  	if !GlobalCLIContext.Quiet {
   331  		mode := globalMinioModeGatewayPrefix + gatewayName
   332  		// Check update mode.
   333  		checkUpdate(mode)
   334  
   335  		// Print a warning message if gateway is not ready for production before the startup banner.
   336  		if !gw.Production() {
   337  			logStartupMessage(color.Yellow("               *** Warning: Not Ready for Production ***"))
   338  		}
   339  
   340  		// Print gateway startup message.
   341  		printGatewayStartupMessage(getAPIEndpoints(), gatewayName)
   342  	}
   343  
   344  	handleSignals()
   345  }