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 }