s3.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package command
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/chrislusf/seaweedfs/weed/pb"
  8. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  9. "github.com/chrislusf/seaweedfs/weed/security"
  10. "github.com/gorilla/mux"
  11. "github.com/chrislusf/seaweedfs/weed/util/log"
  12. "github.com/chrislusf/seaweedfs/weed/s3api"
  13. stats_collect "github.com/chrislusf/seaweedfs/weed/stats"
  14. "github.com/chrislusf/seaweedfs/weed/util"
  15. )
  16. var (
  17. s3StandaloneOptions S3Options
  18. )
  19. type S3Options struct {
  20. filer *string
  21. port *int
  22. config *string
  23. domainName *string
  24. tlsPrivateKey *string
  25. tlsCertificate *string
  26. metricsHttpPort *int
  27. }
  28. func init() {
  29. cmdS3.Run = runS3 // break init cycle
  30. s3StandaloneOptions.filer = cmdS3.Flag.String("filer", "localhost:8888", "filer server address")
  31. s3StandaloneOptions.port = cmdS3.Flag.Int("port", 8333, "s3 server http listen port")
  32. s3StandaloneOptions.domainName = cmdS3.Flag.String("domainName", "", "suffix of the host name in comma separated list, {bucket}.{domainName}")
  33. s3StandaloneOptions.config = cmdS3.Flag.String("config", "", "path to the config file")
  34. s3StandaloneOptions.tlsPrivateKey = cmdS3.Flag.String("key.file", "", "path to the TLS private key file")
  35. s3StandaloneOptions.tlsCertificate = cmdS3.Flag.String("cert.file", "", "path to the TLS certificate file")
  36. s3StandaloneOptions.metricsHttpPort = cmdS3.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
  37. }
  38. var cmdS3 = &Command{
  39. UsageLine: "s3 [-port=8333] [-filer=<ip:port>] [-config=</path/to/config.json>]",
  40. Short: "start a s3 API compatible server that is backed by a filer",
  41. Long: `start a s3 API compatible server that is backed by a filer.
  42. By default, you can use any access key and secret key to access the S3 APIs.
  43. To enable credential based access, create a config.json file similar to this:
  44. {
  45. "identities": [
  46. {
  47. "name": "anonymous",
  48. "actions": [
  49. "Read"
  50. ]
  51. },
  52. {
  53. "name": "some_admin_user",
  54. "credentials": [
  55. {
  56. "accessKey": "some_access_key1",
  57. "secretKey": "some_secret_key1"
  58. }
  59. ],
  60. "actions": [
  61. "Admin",
  62. "Read",
  63. "List",
  64. "Tagging",
  65. "Write"
  66. ]
  67. },
  68. {
  69. "name": "some_read_only_user",
  70. "credentials": [
  71. {
  72. "accessKey": "some_access_key2",
  73. "secretKey": "some_secret_key2"
  74. }
  75. ],
  76. "actions": [
  77. "Read"
  78. ]
  79. },
  80. {
  81. "name": "some_normal_user",
  82. "credentials": [
  83. {
  84. "accessKey": "some_access_key3",
  85. "secretKey": "some_secret_key3"
  86. }
  87. ],
  88. "actions": [
  89. "Read",
  90. "List",
  91. "Tagging",
  92. "Write"
  93. ]
  94. },
  95. {
  96. "name": "user_limited_to_bucket1",
  97. "credentials": [
  98. {
  99. "accessKey": "some_access_key4",
  100. "secretKey": "some_secret_key4"
  101. }
  102. ],
  103. "actions": [
  104. "Read:bucket1",
  105. "List:bucket1",
  106. "Tagging:bucket1",
  107. "Write:bucket1"
  108. ]
  109. }
  110. ]
  111. }
  112. `,
  113. }
  114. func runS3(cmd *Command, args []string) bool {
  115. util.LoadConfiguration("security", false)
  116. go stats_collect.StartMetricsServer(*s3StandaloneOptions.metricsHttpPort)
  117. return s3StandaloneOptions.startS3Server()
  118. }
  119. func (s3opt *S3Options) startS3Server() bool {
  120. filerGrpcAddress, err := pb.ParseFilerGrpcAddress(*s3opt.filer)
  121. if err != nil {
  122. log.Fatal(err)
  123. return false
  124. }
  125. filerBucketsPath := "/buckets"
  126. grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
  127. // metrics read from the filer
  128. var metricsAddress string
  129. var metricsIntervalSec int
  130. for {
  131. err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  132. resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
  133. if err != nil {
  134. return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err)
  135. }
  136. filerBucketsPath = resp.DirBuckets
  137. metricsAddress, metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSec)
  138. log.Infof("S3 read filer buckets dir: %s", filerBucketsPath)
  139. return nil
  140. })
  141. if err != nil {
  142. log.Infof("wait to connect to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress)
  143. time.Sleep(time.Second)
  144. } else {
  145. log.Infof("connected to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress)
  146. break
  147. }
  148. }
  149. go stats_collect.LoopPushingMetric("s3", stats_collect.SourceName(uint32(*s3opt.port)), metricsAddress, metricsIntervalSec)
  150. router := mux.NewRouter().SkipClean(true)
  151. _, s3ApiServer_err := s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{
  152. Filer: *s3opt.filer,
  153. Port: *s3opt.port,
  154. FilerGrpcAddress: filerGrpcAddress,
  155. Config: *s3opt.config,
  156. DomainName: *s3opt.domainName,
  157. BucketsPath: filerBucketsPath,
  158. GrpcDialOption: grpcDialOption,
  159. })
  160. if s3ApiServer_err != nil {
  161. log.Fatalf("S3 API Server startup error: %v", s3ApiServer_err)
  162. }
  163. httpS := &http.Server{Handler: router}
  164. listenAddress := fmt.Sprintf(":%d", *s3opt.port)
  165. s3ApiListener, err := util.NewListener(listenAddress, time.Duration(10)*time.Second)
  166. if err != nil {
  167. log.Fatalf("S3 API Server listener on %s error: %v", listenAddress, err)
  168. }
  169. if *s3opt.tlsPrivateKey != "" {
  170. log.Infof("Start Seaweed S3 API Server %s at https port %d", util.Version(), *s3opt.port)
  171. if err = httpS.ServeTLS(s3ApiListener, *s3opt.tlsCertificate, *s3opt.tlsPrivateKey); err != nil {
  172. log.Fatalf("S3 API Server Fail to serve: %v", err)
  173. }
  174. } else {
  175. log.Infof("Start Seaweed S3 API Server %s at http port %d", util.Version(), *s3opt.port)
  176. if err = httpS.Serve(s3ApiListener); err != nil {
  177. log.Fatalf("S3 API Server Fail to serve: %v", err)
  178. }
  179. }
  180. return true
  181. }