github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/r2ctl/main/main.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package main 22 23 import ( 24 "flag" 25 "fmt" 26 "log" 27 "os" 28 "os/signal" 29 "strconv" 30 "syscall" 31 "time" 32 33 "github.com/m3db/m3/src/cmd/services/r2ctl/config" 34 "github.com/m3db/m3/src/ctl/auth" 35 "github.com/m3db/m3/src/ctl/server/http" 36 "github.com/m3db/m3/src/ctl/service/health" 37 "github.com/m3db/m3/src/ctl/service/r2" 38 "github.com/m3db/m3/src/x/clock" 39 xconfig "github.com/m3db/m3/src/x/config" 40 "github.com/m3db/m3/src/x/config/configflag" 41 "github.com/m3db/m3/src/x/instrument" 42 ) 43 44 const ( 45 portEnvVar = "R2CTL_PORT" 46 r2apiPrefix = "/r2/v1/" 47 gracefulShutdownTimeout = 15 * time.Second 48 ) 49 50 func main() { 51 configOpts := configflag.Options{ 52 ConfigFiles: configflag.FlagStringSlice{Value: []string{"m3ctl.yml"}}, 53 } 54 55 configOpts.Register() 56 57 flag.Parse() 58 59 var cfg config.Configuration 60 if err := configOpts.MainLoad(&cfg, xconfig.Options{}); err != nil { 61 log.Fatalf("error loading config: %v", err) 62 } 63 64 rawLogger, err := cfg.Logging.BuildLogger() 65 if err != nil { 66 log.Fatalf("error creating logger: %v", err) 67 } 68 defer rawLogger.Sync() 69 70 xconfig.WarnOnDeprecation(cfg, rawLogger) 71 72 logger := rawLogger.Sugar() 73 envPort := os.Getenv(portEnvVar) 74 if envPort != "" { 75 if p, err := strconv.Atoi(envPort); err == nil { 76 logger.Infof("using env supplied port var: %s=%d", portEnvVar, p) 77 cfg.HTTP.Port = p 78 } else { 79 logger.Fatalf("%s (%s) is not a valid port number", envPort, portEnvVar) 80 } 81 } 82 83 if cfg.HTTP.Port == 0 { 84 logger.Fatalf("no valid port configured. Can't start.") 85 } 86 87 scope, closer, err := cfg.Metrics.NewRootScope() 88 if err != nil { 89 logger.Fatalf("error creating metrics root scope: %v", err) 90 } 91 defer closer.Close() 92 93 instrumentOpts := instrument.NewOptions(). 94 SetLogger(rawLogger). 95 SetMetricsScope(scope). 96 SetTimerOptions(instrument.TimerOptions{StandardSampleRate: cfg.Metrics.SampleRate()}). 97 SetReportInterval(cfg.Metrics.ReportInterval()) 98 99 // Create R2 store. 100 storeScope := scope.SubScope("r2-store") 101 store, err := cfg.Store.NewR2Store(instrumentOpts.SetMetricsScope(storeScope)) 102 if err != nil { 103 logger.Fatalf("error initializing backing store: %v", err) 104 } 105 106 // Create R2 service. 107 authService := auth.NewNoopAuth() 108 if cfg.Auth != nil { 109 authService = cfg.Auth.NewSimpleAuth() 110 } 111 r2ServiceScope := scope.Tagged(map[string]string{ 112 "service-name": "r2", 113 }) 114 r2ServiceInstrumentOpts := instrumentOpts.SetMetricsScope(r2ServiceScope) 115 r2Service := r2.NewService( 116 r2apiPrefix, 117 authService, 118 store, 119 r2ServiceInstrumentOpts, 120 clock.NewOptions(), 121 ) 122 123 // Create health service. 124 healthServiceScope := scope.Tagged(map[string]string{ 125 "service-name": "health", 126 }) 127 healthServiceInstrumentOpts := instrumentOpts.SetMetricsScope(healthServiceScope) 128 healthService := health.NewService(healthServiceInstrumentOpts) 129 130 // Create HTTP server. 131 listenAddr := fmt.Sprintf("%s:%d", cfg.HTTP.Host, cfg.HTTP.Port) 132 httpServerScope := scope.Tagged(map[string]string{ 133 "server-type": "http", 134 }) 135 httpServerInstrumentOpts := instrumentOpts.SetMetricsScope(httpServerScope) 136 httpServerOpts := cfg.HTTP.NewServerOptions(httpServerInstrumentOpts) 137 server, err := http.NewServer(listenAddr, httpServerOpts, r2Service, healthService) 138 if err != nil { 139 logger.Fatalf("could not create new server: %v", err) 140 } 141 142 logger.Infof("starting http server on: %s", listenAddr) 143 if err := server.ListenAndServe(); err != nil { 144 logger.Fatalf("could not start serving traffic: %v", err) 145 } 146 147 // Handle interrupts. 148 logger.Warnf("interrupt: %v", interrupt()) 149 150 doneCh := make(chan struct{}) 151 go func() { 152 server.Close() 153 logger.Infof("http server closed") 154 doneCh <- struct{}{} 155 }() 156 157 select { 158 case <-doneCh: 159 logger.Infof("clean shutdown") 160 case <-time.After(gracefulShutdownTimeout): 161 logger.Warnf("forced shutdown due to timeout after waiting for %v", gracefulShutdownTimeout) 162 } 163 } 164 165 func interrupt() error { 166 c := make(chan os.Signal, 1) 167 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 168 return fmt.Errorf("%s", <-c) 169 }