You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							406 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							406 lines
						
					
					
						
							14 KiB
						
					
					
				
								package command
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"context"
							 | 
						|
									"crypto/tls"
							 | 
						|
									"crypto/x509"
							 | 
						|
									"fmt"
							 | 
						|
									"io/ioutil"
							 | 
						|
									"net"
							 | 
						|
									"os"
							 | 
						|
									"runtime"
							 | 
						|
									"strings"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/gorilla/mux"
							 | 
						|
									"google.golang.org/grpc/credentials/tls/certprovider"
							 | 
						|
									"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
							 | 
						|
									"google.golang.org/grpc/reflection"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/s3_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/security"
							 | 
						|
									stats_collect "github.com/seaweedfs/seaweedfs/weed/stats"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util/version"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								var (
							 | 
						|
									s3StandaloneOptions S3Options
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type S3Options struct {
							 | 
						|
									filer                     *string
							 | 
						|
									bindIp                    *string
							 | 
						|
									port                      *int
							 | 
						|
									portHttps                 *int
							 | 
						|
									portGrpc                  *int
							 | 
						|
									config                    *string
							 | 
						|
									iamConfig                 *string
							 | 
						|
									domainName                *string
							 | 
						|
									allowedOrigins            *string
							 | 
						|
									tlsPrivateKey             *string
							 | 
						|
									tlsCertificate            *string
							 | 
						|
									tlsCACertificate          *string
							 | 
						|
									tlsVerifyClientCert       *bool
							 | 
						|
									metricsHttpPort           *int
							 | 
						|
									metricsHttpIp             *string
							 | 
						|
									allowEmptyFolder          *bool
							 | 
						|
									allowDeleteBucketNotEmpty *bool
							 | 
						|
									auditLogConfig            *string
							 | 
						|
									localFilerSocket          *string
							 | 
						|
									dataCenter                *string
							 | 
						|
									localSocket               *string
							 | 
						|
									certProvider              certprovider.Provider
							 | 
						|
									idleTimeout               *int
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func init() {
							 | 
						|
									cmdS3.Run = runS3 // break init cycle
							 | 
						|
									s3StandaloneOptions.filer = cmdS3.Flag.String("filer", "localhost:8888", "filer server address")
							 | 
						|
									s3StandaloneOptions.bindIp = cmdS3.Flag.String("ip.bind", "", "ip address to bind to. Default to localhost.")
							 | 
						|
									s3StandaloneOptions.port = cmdS3.Flag.Int("port", 8333, "s3 server http listen port")
							 | 
						|
									s3StandaloneOptions.portHttps = cmdS3.Flag.Int("port.https", 0, "s3 server https listen port")
							 | 
						|
									s3StandaloneOptions.portGrpc = cmdS3.Flag.Int("port.grpc", 0, "s3 server grpc listen port")
							 | 
						|
									s3StandaloneOptions.domainName = cmdS3.Flag.String("domainName", "", "suffix of the host name in comma separated list, {bucket}.{domainName}")
							 | 
						|
									s3StandaloneOptions.allowedOrigins = cmdS3.Flag.String("allowedOrigins", "*", "comma separated list of allowed origins")
							 | 
						|
									s3StandaloneOptions.dataCenter = cmdS3.Flag.String("dataCenter", "", "prefer to read and write to volumes in this data center")
							 | 
						|
									s3StandaloneOptions.config = cmdS3.Flag.String("config", "", "path to the config file")
							 | 
						|
									s3StandaloneOptions.iamConfig = cmdS3.Flag.String("iam.config", "", "path to the advanced IAM config file")
							 | 
						|
									s3StandaloneOptions.auditLogConfig = cmdS3.Flag.String("auditLogConfig", "", "path to the audit log config file")
							 | 
						|
									s3StandaloneOptions.tlsPrivateKey = cmdS3.Flag.String("key.file", "", "path to the TLS private key file")
							 | 
						|
									s3StandaloneOptions.tlsCertificate = cmdS3.Flag.String("cert.file", "", "path to the TLS certificate file")
							 | 
						|
									s3StandaloneOptions.tlsCACertificate = cmdS3.Flag.String("cacert.file", "", "path to the TLS CA certificate file")
							 | 
						|
									s3StandaloneOptions.tlsVerifyClientCert = cmdS3.Flag.Bool("tlsVerifyClientCert", false, "whether to verify the client's certificate")
							 | 
						|
									s3StandaloneOptions.metricsHttpPort = cmdS3.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
							 | 
						|
									s3StandaloneOptions.metricsHttpIp = cmdS3.Flag.String("metricsIp", "", "metrics listen ip. If empty, default to same as -ip.bind option.")
							 | 
						|
									s3StandaloneOptions.allowEmptyFolder = cmdS3.Flag.Bool("allowEmptyFolder", true, "allow empty folders")
							 | 
						|
									s3StandaloneOptions.allowDeleteBucketNotEmpty = cmdS3.Flag.Bool("allowDeleteBucketNotEmpty", true, "allow recursive deleting all entries along with bucket")
							 | 
						|
									s3StandaloneOptions.localFilerSocket = cmdS3.Flag.String("localFilerSocket", "", "local filer socket path")
							 | 
						|
									s3StandaloneOptions.localSocket = cmdS3.Flag.String("localSocket", "", "default to /tmp/seaweedfs-s3-<port>.sock")
							 | 
						|
									s3StandaloneOptions.idleTimeout = cmdS3.Flag.Int("idleTimeout", 10, "connection idle seconds")
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var cmdS3 = &Command{
							 | 
						|
									UsageLine: "s3 [-port=8333] [-filer=<ip:port>] [-config=</path/to/config.json>]",
							 | 
						|
									Short:     "start a s3 API compatible server that is backed by a filer",
							 | 
						|
									Long: `start a s3 API compatible server that is backed by a filer.
							 | 
						|
								
							 | 
						|
									By default, you can use any access key and secret key to access the S3 APIs.
							 | 
						|
									To enable credential based access, create a config.json file similar to this:
							 | 
						|
								
							 | 
						|
								{
							 | 
						|
								  "identities": [
							 | 
						|
								    {
							 | 
						|
								      "name": "anonymous",
							 | 
						|
								      "actions": [
							 | 
						|
								        "Read"
							 | 
						|
								      ]
							 | 
						|
								    },
							 | 
						|
								    {
							 | 
						|
								      "name": "some_admin_user",
							 | 
						|
								      "credentials": [
							 | 
						|
								        {
							 | 
						|
								          "accessKey": "some_access_key1",
							 | 
						|
								          "secretKey": "some_secret_key1"
							 | 
						|
								        }
							 | 
						|
								      ],
							 | 
						|
								      "actions": [
							 | 
						|
								        "Admin",
							 | 
						|
								        "Read",
							 | 
						|
								        "List",
							 | 
						|
								        "Tagging",
							 | 
						|
								        "Write"
							 | 
						|
								      ]
							 | 
						|
								    },
							 | 
						|
								    {
							 | 
						|
								      "name": "some_read_only_user",
							 | 
						|
								      "credentials": [
							 | 
						|
								        {
							 | 
						|
								          "accessKey": "some_access_key2",
							 | 
						|
								          "secretKey": "some_secret_key2"
							 | 
						|
								        }
							 | 
						|
								      ],
							 | 
						|
								      "actions": [
							 | 
						|
								        "Read"
							 | 
						|
								      ]
							 | 
						|
								    },
							 | 
						|
								    {
							 | 
						|
								      "name": "some_normal_user",
							 | 
						|
								      "credentials": [
							 | 
						|
								        {
							 | 
						|
								          "accessKey": "some_access_key3",
							 | 
						|
								          "secretKey": "some_secret_key3"
							 | 
						|
								        }
							 | 
						|
								      ],
							 | 
						|
								      "actions": [
							 | 
						|
								        "Read",
							 | 
						|
								        "List",
							 | 
						|
								        "Tagging",
							 | 
						|
								        "Write"
							 | 
						|
								      ]
							 | 
						|
								    },
							 | 
						|
								    {
							 | 
						|
								      "name": "user_limited_to_bucket1",
							 | 
						|
								      "credentials": [
							 | 
						|
								        {
							 | 
						|
								          "accessKey": "some_access_key4",
							 | 
						|
								          "secretKey": "some_secret_key4"
							 | 
						|
								        }
							 | 
						|
								      ],
							 | 
						|
								      "actions": [
							 | 
						|
								        "Read:bucket1",
							 | 
						|
								        "List:bucket1",
							 | 
						|
								        "Tagging:bucket1",
							 | 
						|
								        "Write:bucket1"
							 | 
						|
								      ]
							 | 
						|
								    }
							 | 
						|
								  ]
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
									Alternatively, you can use environment variables as fallback admin credentials:
							 | 
						|
								
							 | 
						|
									AWS_ACCESS_KEY_ID=your_access_key AWS_SECRET_ACCESS_KEY=your_secret_key weed s3
							 | 
						|
								
							 | 
						|
									Environment variables are only used when no S3 configuration file is provided
							 | 
						|
									and no configuration is available from the filer. This provides a simple way
							 | 
						|
									to get started without requiring configuration files.
							 | 
						|
								
							 | 
						|
								`,
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func runS3(cmd *Command, args []string) bool {
							 | 
						|
								
							 | 
						|
									util.LoadSecurityConfiguration()
							 | 
						|
								
							 | 
						|
									switch {
							 | 
						|
									case *s3StandaloneOptions.metricsHttpIp != "":
							 | 
						|
										// noting to do, use s3StandaloneOptions.metricsHttpIp
							 | 
						|
									case *s3StandaloneOptions.bindIp != "":
							 | 
						|
										*s3StandaloneOptions.metricsHttpIp = *s3StandaloneOptions.bindIp
							 | 
						|
									}
							 | 
						|
									go stats_collect.StartMetricsServer(*s3StandaloneOptions.metricsHttpIp, *s3StandaloneOptions.metricsHttpPort)
							 | 
						|
								
							 | 
						|
									return s3StandaloneOptions.startS3Server()
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetCertificateWithUpdate Auto refreshing TSL certificate
							 | 
						|
								func (s3opt *S3Options) GetCertificateWithUpdate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
							 | 
						|
									certs, err := s3opt.certProvider.KeyMaterial(context.Background())
							 | 
						|
									if certs == nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
									return &certs.Certs[0], err
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s3opt *S3Options) startS3Server() bool {
							 | 
						|
								
							 | 
						|
									filerAddress := pb.ServerAddress(*s3opt.filer)
							 | 
						|
								
							 | 
						|
									filerBucketsPath := "/buckets"
							 | 
						|
									filerGroup := ""
							 | 
						|
								
							 | 
						|
									grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
							 | 
						|
								
							 | 
						|
									// metrics read from the filer
							 | 
						|
									var metricsAddress string
							 | 
						|
									var metricsIntervalSec int
							 | 
						|
								
							 | 
						|
									for {
							 | 
						|
										err := pb.WithGrpcFilerClient(false, 0, filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
							 | 
						|
											resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
							 | 
						|
											if err != nil {
							 | 
						|
												return fmt.Errorf("get filer %s configuration: %v", filerAddress, err)
							 | 
						|
											}
							 | 
						|
											filerBucketsPath = resp.DirBuckets
							 | 
						|
											filerGroup = resp.FilerGroup
							 | 
						|
											metricsAddress, metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSec)
							 | 
						|
											glog.V(0).Infof("S3 read filer buckets dir: %s", filerBucketsPath)
							 | 
						|
											return nil
							 | 
						|
										})
							 | 
						|
										if err != nil {
							 | 
						|
											glog.V(0).Infof("wait to connect to filer %s grpc address %s", *s3opt.filer, filerAddress.ToGrpcAddress())
							 | 
						|
											time.Sleep(time.Second)
							 | 
						|
										} else {
							 | 
						|
											glog.V(0).Infof("connected to filer %s grpc address %s", *s3opt.filer, filerAddress.ToGrpcAddress())
							 | 
						|
											break
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									go stats_collect.LoopPushingMetric("s3", stats_collect.SourceName(uint32(*s3opt.port)), metricsAddress, metricsIntervalSec)
							 | 
						|
								
							 | 
						|
									router := mux.NewRouter().SkipClean(true)
							 | 
						|
									var localFilerSocket string
							 | 
						|
									if s3opt.localFilerSocket != nil {
							 | 
						|
										localFilerSocket = *s3opt.localFilerSocket
							 | 
						|
									}
							 | 
						|
									var s3ApiServer *s3api.S3ApiServer
							 | 
						|
									var s3ApiServer_err error
							 | 
						|
								
							 | 
						|
									// Create S3 server with optional advanced IAM integration
							 | 
						|
									var iamConfigPath string
							 | 
						|
									if s3opt.iamConfig != nil && *s3opt.iamConfig != "" {
							 | 
						|
										iamConfigPath = *s3opt.iamConfig
							 | 
						|
										glog.V(0).Infof("Starting S3 API Server with advanced IAM integration")
							 | 
						|
									} else {
							 | 
						|
										glog.V(0).Infof("Starting S3 API Server with standard IAM")
							 | 
						|
									}
							 | 
						|
									
							 | 
						|
									s3ApiServer, s3ApiServer_err = s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{
							 | 
						|
										Filer:                     filerAddress,
							 | 
						|
										Port:                      *s3opt.port,
							 | 
						|
										Config:                    *s3opt.config,
							 | 
						|
										DomainName:                *s3opt.domainName,
							 | 
						|
										AllowedOrigins:            strings.Split(*s3opt.allowedOrigins, ","),
							 | 
						|
										BucketsPath:               filerBucketsPath,
							 | 
						|
										GrpcDialOption:            grpcDialOption,
							 | 
						|
										AllowEmptyFolder:          *s3opt.allowEmptyFolder,
							 | 
						|
										AllowDeleteBucketNotEmpty: *s3opt.allowDeleteBucketNotEmpty,
							 | 
						|
										LocalFilerSocket:          localFilerSocket,
							 | 
						|
										DataCenter:                *s3opt.dataCenter,
							 | 
						|
										FilerGroup:                filerGroup,
							 | 
						|
										IamConfig:                 iamConfigPath, // Advanced IAM config (optional)
							 | 
						|
									})
							 | 
						|
									if s3ApiServer_err != nil {
							 | 
						|
										glog.Fatalf("S3 API Server startup error: %v", s3ApiServer_err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if *s3opt.portGrpc == 0 {
							 | 
						|
										*s3opt.portGrpc = 10000 + *s3opt.port
							 | 
						|
									}
							 | 
						|
									if *s3opt.bindIp == "" {
							 | 
						|
										*s3opt.bindIp = "localhost"
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if runtime.GOOS != "windows" {
							 | 
						|
										localSocket := *s3opt.localSocket
							 | 
						|
										if localSocket == "" {
							 | 
						|
											localSocket = fmt.Sprintf("/tmp/seaweedfs-s3-%d.sock", *s3opt.port)
							 | 
						|
										}
							 | 
						|
										if err := os.Remove(localSocket); err != nil && !os.IsNotExist(err) {
							 | 
						|
											glog.Fatalf("Failed to remove %s, error: %s", localSocket, err.Error())
							 | 
						|
										}
							 | 
						|
										go func() {
							 | 
						|
											// start on local unix socket
							 | 
						|
											s3SocketListener, err := net.Listen("unix", localSocket)
							 | 
						|
											if err != nil {
							 | 
						|
												glog.Fatalf("Failed to listen on %s: %v", localSocket, err)
							 | 
						|
											}
							 | 
						|
											newHttpServer(router, nil).Serve(s3SocketListener)
							 | 
						|
										}()
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									listenAddress := fmt.Sprintf("%s:%d", *s3opt.bindIp, *s3opt.port)
							 | 
						|
									s3ApiListener, s3ApiLocalListener, err := util.NewIpAndLocalListeners(
							 | 
						|
										*s3opt.bindIp, *s3opt.port, time.Duration(*s3opt.idleTimeout)*time.Second)
							 | 
						|
									if err != nil {
							 | 
						|
										glog.Fatalf("S3 API Server listener on %s error: %v", listenAddress, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if len(*s3opt.auditLogConfig) > 0 {
							 | 
						|
										s3err.InitAuditLog(*s3opt.auditLogConfig)
							 | 
						|
										if s3err.Logger != nil {
							 | 
						|
											defer s3err.Logger.Close()
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// starting grpc server
							 | 
						|
									grpcPort := *s3opt.portGrpc
							 | 
						|
									grpcL, grpcLocalL, err := util.NewIpAndLocalListeners(*s3opt.bindIp, grpcPort, 0)
							 | 
						|
									if err != nil {
							 | 
						|
										glog.Fatalf("s3 failed to listen on grpc port %d: %v", grpcPort, err)
							 | 
						|
									}
							 | 
						|
									grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.s3"))
							 | 
						|
									s3_pb.RegisterSeaweedS3Server(grpcS, s3ApiServer)
							 | 
						|
									reflection.Register(grpcS)
							 | 
						|
									if grpcLocalL != nil {
							 | 
						|
										go grpcS.Serve(grpcLocalL)
							 | 
						|
									}
							 | 
						|
									go grpcS.Serve(grpcL)
							 | 
						|
								
							 | 
						|
									if *s3opt.tlsPrivateKey != "" {
							 | 
						|
										pemfileOptions := pemfile.Options{
							 | 
						|
											CertFile:        *s3opt.tlsCertificate,
							 | 
						|
											KeyFile:         *s3opt.tlsPrivateKey,
							 | 
						|
											RefreshDuration: security.CredRefreshingInterval,
							 | 
						|
										}
							 | 
						|
										if s3opt.certProvider, err = pemfile.NewProvider(pemfileOptions); err != nil {
							 | 
						|
											glog.Fatalf("pemfile.NewProvider(%v) failed: %v", pemfileOptions, err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										caCertPool := x509.NewCertPool()
							 | 
						|
										if *s3opt.tlsCACertificate != "" {
							 | 
						|
											// load CA certificate file and add it to list of client CAs
							 | 
						|
											caCertFile, err := ioutil.ReadFile(*s3opt.tlsCACertificate)
							 | 
						|
											if err != nil {
							 | 
						|
												glog.Fatalf("error reading CA certificate: %v", err)
							 | 
						|
											}
							 | 
						|
											caCertPool.AppendCertsFromPEM(caCertFile)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										clientAuth := tls.NoClientCert
							 | 
						|
										if *s3opt.tlsVerifyClientCert {
							 | 
						|
											clientAuth = tls.RequireAndVerifyClientCert
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										tlsConfig := &tls.Config{
							 | 
						|
											GetCertificate: s3opt.GetCertificateWithUpdate,
							 | 
						|
											ClientAuth:     clientAuth,
							 | 
						|
											ClientCAs:      caCertPool,
							 | 
						|
										}
							 | 
						|
										err = security.FixTlsConfig(util.GetViper(), tlsConfig)
							 | 
						|
										if err != nil {
							 | 
						|
											glog.Fatalf("error with tls config: %v", err)
							 | 
						|
										}
							 | 
						|
										if *s3opt.portHttps == 0 {
							 | 
						|
											glog.V(0).Infof("Start Seaweed S3 API Server %s at https port %d", version.Version(), *s3opt.port)
							 | 
						|
											if s3ApiLocalListener != nil {
							 | 
						|
												go func() {
							 | 
						|
													if err = newHttpServer(router, tlsConfig).ServeTLS(s3ApiLocalListener, "", ""); err != nil {
							 | 
						|
														glog.Fatalf("S3 API Server Fail to serve: %v", err)
							 | 
						|
													}
							 | 
						|
												}()
							 | 
						|
											}
							 | 
						|
											if err = newHttpServer(router, tlsConfig).ServeTLS(s3ApiListener, "", ""); err != nil {
							 | 
						|
												glog.Fatalf("S3 API Server Fail to serve: %v", err)
							 | 
						|
											}
							 | 
						|
										} else {
							 | 
						|
											glog.V(0).Infof("Start Seaweed S3 API Server %s at https port %d", version.Version(), *s3opt.portHttps)
							 | 
						|
											s3ApiListenerHttps, s3ApiLocalListenerHttps, _ := util.NewIpAndLocalListeners(
							 | 
						|
												*s3opt.bindIp, *s3opt.portHttps, time.Duration(*s3opt.idleTimeout)*time.Second)
							 | 
						|
											if s3ApiLocalListenerHttps != nil {
							 | 
						|
												go func() {
							 | 
						|
													if err = newHttpServer(router, tlsConfig).ServeTLS(s3ApiLocalListenerHttps, "", ""); err != nil {
							 | 
						|
														glog.Fatalf("S3 API Server Fail to serve: %v", err)
							 | 
						|
													}
							 | 
						|
												}()
							 | 
						|
											}
							 | 
						|
											go func() {
							 | 
						|
												if err = newHttpServer(router, tlsConfig).ServeTLS(s3ApiListenerHttps, "", ""); err != nil {
							 | 
						|
													glog.Fatalf("S3 API Server Fail to serve: %v", err)
							 | 
						|
												}
							 | 
						|
											}()
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									if *s3opt.tlsPrivateKey == "" || *s3opt.portHttps > 0 {
							 | 
						|
										glog.V(0).Infof("Start Seaweed S3 API Server %s at http port %d", version.Version(), *s3opt.port)
							 | 
						|
										if s3ApiLocalListener != nil {
							 | 
						|
											go func() {
							 | 
						|
												if err = newHttpServer(router, nil).Serve(s3ApiLocalListener); err != nil {
							 | 
						|
													glog.Fatalf("S3 API Server Fail to serve: %v", err)
							 | 
						|
												}
							 | 
						|
											}()
							 | 
						|
										}
							 | 
						|
										if err = newHttpServer(router, nil).Serve(s3ApiListener); err != nil {
							 | 
						|
											glog.Fatalf("S3 API Server Fail to serve: %v", err)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return true
							 | 
						|
								
							 | 
						|
								}
							 |