github.com/go-graphite/carbonapi@v0.17.0/cmd/carbonapi/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "expvar" 6 "flag" 7 "log" 8 "net" 9 "net/http" 10 "net/http/pprof" 11 "os" 12 "os/signal" 13 "sync" 14 "syscall" 15 "time" 16 17 "github.com/gorilla/handlers" 18 "github.com/lomik/zapwriter" 19 "go.uber.org/zap" 20 21 "github.com/go-graphite/carbonapi/pkg/tlsconfig" 22 23 "github.com/go-graphite/carbonapi/cmd/carbonapi/config" 24 "github.com/go-graphite/carbonapi/cmd/carbonapi/helper" 25 carbonapiHttp "github.com/go-graphite/carbonapi/cmd/carbonapi/http" 26 "github.com/go-graphite/carbonapi/internal/dns" 27 ) 28 29 // BuildVersion is provided to be overridden at build time. Eg. go build -ldflags -X 'main.BuildVersion=...' 30 var BuildVersion = "(development build)" 31 32 func main() { 33 err := zapwriter.ApplyConfig([]zapwriter.Config{config.DefaultLoggerConfig}) 34 if err != nil { 35 log.Fatal("Failed to initialize logger with default configuration") 36 } 37 logger := zapwriter.Logger("main") 38 39 configPath := flag.String("config", "", "Path to the `config file`.") 40 checkConfig := flag.Bool("check-config", false, "Check config file and exit.") 41 exactConfig := flag.Bool("exact-config", false, "Ensure that all config params are contained in the target struct.") 42 envPrefix := flag.String("envprefix", "CARBONAPI", "Prefix for environment variables override") 43 if *envPrefix == "(empty)" { 44 *envPrefix = "" 45 } 46 if *envPrefix == "" { 47 logger.Warn("empty prefix is not recommended due to possible collisions with OS environment variables") 48 } 49 flag.Parse() 50 config.SetUpViper(logger, configPath, *exactConfig, *envPrefix) 51 if *checkConfig { 52 os.Exit(0) 53 } 54 config.SetUpConfigUpstreams(logger) 55 config.SetUpConfig(logger, BuildVersion) 56 carbonapiHttp.SetupMetrics(logger) 57 setupGraphiteMetrics(logger) 58 59 if config.Config.UseCachingDNSResolver { 60 logger.Info("will use custom caching dns resolver") 61 dns.UseDNSCache(config.Config.CachingDNSRefreshTime) 62 } 63 64 if err := config.Config.SetZipper(newZipper(carbonapiHttp.ZipperStats, &config.Config.Upstreams, config.Config.IgnoreClientTimeout, zapwriter.Logger("zipper"))); err != nil { 65 logger.Fatal("failed to setup zipper", 66 zap.Error(err), 67 ) 68 } 69 70 wg := sync.WaitGroup{} 71 serve := func(listen config.Listener, handler http.Handler) { 72 l := &net.ListenConfig{Control: helper.ReusePort} 73 h, p, err := net.SplitHostPort(listen.Address) 74 if err != nil { 75 logger.Fatal("failed to split address", 76 zap.String("address", listen.Address), 77 zap.Error(err), 78 ) 79 } 80 any := false 81 if h == "" { 82 h = "[::]" 83 any = true 84 } 85 ips, err := net.LookupIP(h) 86 if err != nil { 87 // Fallback for a case where machine doesn't have ipv6 at all 88 if any { 89 h = "0.0.0.0" 90 ips, err = net.LookupIP(h) 91 } 92 if err != nil { 93 logger.Fatal("failed to resolve address", 94 zap.String("address", h), 95 zap.String("port", p), 96 zap.Error(err), 97 ) 98 } 99 } 100 // Resolve named ports 101 port, err := net.LookupPort("tcp", p) 102 if err != nil { 103 logger.Fatal("failed to resolve port", 104 zap.String("address", h), 105 zap.String("port", p), 106 zap.Error(err), 107 ) 108 } 109 httpLogger, err := zap.NewStdLogAt(zapwriter.Logger("http"), zap.WarnLevel) 110 if err != nil { 111 logger.Fatal("failed to set up http server logger", 112 zap.Error(err), 113 ) 114 } 115 116 servers := make([]*http.Server, 0) 117 118 for _, ip := range ips { 119 address := (&net.TCPAddr{IP: ip, Port: port}).String() 120 s := &http.Server{ 121 Addr: address, 122 Handler: handler, 123 ErrorLog: httpLogger, 124 } 125 servers = append(servers, s) 126 isTLS := false 127 if len(listen.ServerTLSConfig.CACertFiles) > 0 { 128 tlsConfig, warns, err := tlsconfig.ParseServerTLSConfig(&listen.ServerTLSConfig, &listen.ClientTLSConfig) 129 if err != nil { 130 logger.Fatal("failed to initialize TLS", 131 zap.Error(err), 132 ) 133 } 134 if len(warns) != 0 { 135 logger.Warn("insecure ciphers are in-use", 136 zap.Strings("insecure_ciphers", warns), 137 ) 138 } 139 s.TLSConfig = tlsConfig 140 isTLS = true 141 } 142 143 listener, err := l.Listen(context.Background(), "tcp", address) 144 if err != nil { 145 logger.Fatal("failed to start http server", 146 zap.Error(err), 147 ) 148 } 149 wg.Add(1) 150 go func(listener net.Listener, isTLS bool) { 151 if isTLS { 152 err = s.ServeTLS(listener, "", "") 153 } else { 154 err = s.Serve(listener) 155 } 156 157 if err != nil && err != http.ErrServerClosed { 158 logger.Error("failed to start http server", 159 zap.Error(err), 160 ) 161 } 162 163 wg.Done() 164 }(listener, isTLS) 165 } 166 167 go func() { 168 stop := make(chan os.Signal, 1) 169 signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT) 170 <-stop 171 logger.Info("stoping carbonapi") 172 // initiating the shutdown 173 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 174 for _, s := range servers { 175 s.Shutdown(ctx) 176 } 177 cancel() 178 }() 179 180 } 181 182 if config.Config.Expvar.Enabled { 183 if config.Config.Expvar.Listen != "" && config.Config.Expvar.Listen != config.Config.Listeners[0].Address { 184 r := http.NewServeMux() 185 r.HandleFunc(config.Config.Prefix+"/debug/vars", expvar.Handler().ServeHTTP) 186 if config.Config.Expvar.PProfEnabled { 187 r.HandleFunc(config.Config.Prefix+"/debug/pprof/", pprof.Index) 188 r.HandleFunc(config.Config.Prefix+"/debug/pprof/cmdline", pprof.Cmdline) 189 r.HandleFunc(config.Config.Prefix+"/debug/pprof/profile", pprof.Profile) 190 r.HandleFunc(config.Config.Prefix+"/debug/pprof/symbol", pprof.Symbol) 191 r.HandleFunc(config.Config.Prefix+"/debug/pprof/trace", pprof.Trace) 192 } 193 194 handler := handlers.CompressHandler(r) 195 handler = handlers.CORS()(handler) 196 handler = handlers.ProxyHeaders(handler) 197 198 logger.Info("expvar handler will listen on a separate address/port", 199 zap.String("expvar_listen", config.Config.Expvar.Listen), 200 zap.Bool("pprof_enabled", config.Config.Expvar.PProfEnabled), 201 ) 202 203 listener := config.Listener{ 204 Address: config.Config.Expvar.Listen, 205 } 206 serve(listener, handler) 207 } 208 } 209 210 r := carbonapiHttp.InitHandlers(config.Config.HeadersToPass, config.Config.HeadersToLog) 211 handler := handlers.CompressHandler(r) 212 handler = handlers.CORS()(handler) 213 handler = handlers.ProxyHeaders(handler) 214 215 for _, listener := range config.Config.Listeners { 216 serve(listener, handler) 217 } 218 219 wg.Wait() 220 221 if g != nil { 222 g.Stop() 223 } 224 if carbonapiHttp.Gstatsd != nil { 225 carbonapiHttp.Gstatsd.Close() 226 } 227 }