github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/server/server.go (about) 1 package server 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "net" 8 "net/http" 9 "os" 10 goRuntime "runtime" 11 "strings" 12 "sync" 13 "syscall" 14 "time" 15 16 "github.com/containers/podman/v2/libpod" 17 "github.com/containers/podman/v2/libpod/shutdown" 18 "github.com/containers/podman/v2/pkg/api/handlers" 19 "github.com/containers/podman/v2/pkg/api/server/idle" 20 "github.com/coreos/go-systemd/v22/activation" 21 "github.com/coreos/go-systemd/v22/daemon" 22 "github.com/gorilla/mux" 23 "github.com/gorilla/schema" 24 "github.com/pkg/errors" 25 "github.com/sirupsen/logrus" 26 ) 27 28 type APIServer struct { 29 http.Server // The HTTP work happens here 30 *schema.Decoder // Decoder for Query parameters to structs 31 context.Context // Context to carry objects to handlers 32 *libpod.Runtime // Where the real work happens 33 net.Listener // mux for routing HTTP API calls to libpod routines 34 context.CancelFunc // Stop APIServer 35 idleTracker *idle.Tracker // Track connections to support idle shutdown 36 pprof *http.Server // Sidecar http server for providing performance data 37 } 38 39 // Number of seconds to wait for next request, if exceeded shutdown server 40 const ( 41 DefaultServiceDuration = 300 * time.Second 42 UnlimitedServiceDuration = 0 * time.Second 43 ) 44 45 // shutdownOnce ensures Shutdown() may safely be called from several go routines 46 var shutdownOnce sync.Once 47 48 // NewServer will create and configure a new API server with all defaults 49 func NewServer(runtime *libpod.Runtime) (*APIServer, error) { 50 return newServer(runtime, DefaultServiceDuration, nil) 51 } 52 53 // NewServerWithSettings will create and configure a new API server using provided settings 54 func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { 55 return newServer(runtime, duration, listener) 56 } 57 58 func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { 59 // If listener not provided try socket activation protocol 60 if listener == nil { 61 if _, found := os.LookupEnv("LISTEN_PID"); !found { 62 return nil, errors.Errorf("Cannot create API Server, no listener provided and socket activation protocol is not active.") 63 } 64 65 listeners, err := activation.Listeners() 66 if err != nil { 67 return nil, errors.Wrap(err, "Cannot retrieve file descriptors from systemd") 68 } 69 if len(listeners) != 1 { 70 return nil, errors.Errorf("Wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners)) 71 } 72 listener = &listeners[0] 73 } 74 75 logrus.Infof("API server listening on %q", (*listener).Addr()) 76 router := mux.NewRouter().UseEncodedPath() 77 idle := idle.NewTracker(duration) 78 79 server := APIServer{ 80 Server: http.Server{ 81 Handler: router, 82 ReadHeaderTimeout: 20 * time.Second, 83 IdleTimeout: duration * 2, 84 ConnState: idle.ConnState, 85 ErrorLog: log.New(logrus.StandardLogger().Out, "", 0), 86 }, 87 Decoder: handlers.NewAPIDecoder(), 88 idleTracker: idle, 89 Listener: *listener, 90 Runtime: runtime, 91 } 92 93 router.NotFoundHandler = http.HandlerFunc( 94 func(w http.ResponseWriter, r *http.Request) { 95 // We can track user errors... 96 logrus.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusNotFound, http.StatusText(http.StatusNotFound), r.Method, r.URL.String()) 97 http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) 98 }, 99 ) 100 101 router.MethodNotAllowedHandler = http.HandlerFunc( 102 func(w http.ResponseWriter, r *http.Request) { 103 // We can track user errors... 104 logrus.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed), r.Method, r.URL.String()) 105 http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) 106 }, 107 ) 108 109 for _, fn := range []func(*mux.Router) error{ 110 server.registerAuthHandlers, 111 server.registerAchiveHandlers, 112 server.registerContainersHandlers, 113 server.registerDistributionHandlers, 114 server.registerEventsHandlers, 115 server.registerExecHandlers, 116 server.registerGenerateHandlers, 117 server.registerHealthCheckHandlers, 118 server.registerImagesHandlers, 119 server.registerInfoHandlers, 120 server.registerManifestHandlers, 121 server.registerMonitorHandlers, 122 server.registerNetworkHandlers, 123 server.registerPingHandlers, 124 server.registerPlayHandlers, 125 server.registerPluginsHandlers, 126 server.registerPodsHandlers, 127 server.RegisterSwaggerHandlers, 128 server.registerSwarmHandlers, 129 server.registerSystemHandlers, 130 server.registerVersionHandlers, 131 server.registerVolumeHandlers, 132 } { 133 if err := fn(router); err != nil { 134 return nil, err 135 } 136 } 137 138 if logrus.IsLevelEnabled(logrus.DebugLevel) { 139 router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint 140 path, err := route.GetPathTemplate() 141 if err != nil { 142 path = "<N/A>" 143 } 144 methods, err := route.GetMethods() 145 if err != nil { 146 methods = []string{"<N/A>"} 147 } 148 logrus.Debugf("Methods: %6s Path: %s", strings.Join(methods, ", "), path) 149 return nil 150 }) 151 } 152 153 return &server, nil 154 } 155 156 // If the NOTIFY_SOCKET is set, communicate the PID and readiness, and 157 // further unset NOTIFY_SOCKET to prevent containers from sending 158 // messages and unset INVOCATION_ID so conmon and containers are in 159 // the correct cgroup. 160 func setupSystemd() { 161 if len(os.Getenv("NOTIFY_SOCKET")) == 0 { 162 return 163 } 164 payload := fmt.Sprintf("MAINPID=%d", os.Getpid()) 165 payload += "\n" 166 payload += daemon.SdNotifyReady 167 if sent, err := daemon.SdNotify(true, payload); err != nil { 168 logrus.Errorf("Error notifying systemd of Conmon PID: %s", err.Error()) 169 } else if sent { 170 logrus.Debugf("Notify sent successfully") 171 } 172 173 if err := os.Unsetenv("INVOCATION_ID"); err != nil { 174 logrus.Errorf("Error unsetting INVOCATION_ID: %s", err.Error()) 175 } 176 } 177 178 // Serve starts responding to HTTP requests. 179 func (s *APIServer) Serve() error { 180 setupSystemd() 181 182 // Start the shutdown signal handler. 183 if err := shutdown.Start(); err != nil { 184 return err 185 } 186 if err := shutdown.Register("server", func(sig os.Signal) error { 187 return s.Shutdown() 188 }); err != nil { 189 return err 190 } 191 192 errChan := make(chan error, 1) 193 194 go func() { 195 <-s.idleTracker.Done() 196 logrus.Debugf("API Server idle for %s", s.idleTracker.Duration.Round(time.Second).String()) 197 _ = s.Shutdown() 198 }() 199 200 if logrus.IsLevelEnabled(logrus.DebugLevel) { 201 go func() { 202 pprofMux := mux.NewRouter() 203 pprofMux.PathPrefix("/debug/pprof").Handler(http.DefaultServeMux) 204 goRuntime.SetMutexProfileFraction(1) 205 goRuntime.SetBlockProfileRate(1) 206 s.pprof = &http.Server{Addr: "localhost:8888", Handler: pprofMux} 207 err := s.pprof.ListenAndServe() 208 if err != nil && err != http.ErrServerClosed { 209 logrus.Warn("Profiler Service failed: " + err.Error()) 210 } 211 }() 212 } 213 214 // Before we start serving, ensure umask is properly set for container 215 // creation. 216 _ = syscall.Umask(0022) 217 218 go func() { 219 err := s.Server.Serve(s.Listener) 220 if err != nil && err != http.ErrServerClosed { 221 errChan <- errors.Wrap(err, "failed to start API server") 222 return 223 } 224 errChan <- nil 225 }() 226 227 return <-errChan 228 } 229 230 // Shutdown is a clean shutdown waiting on existing clients 231 func (s *APIServer) Shutdown() error { 232 if s.idleTracker.Duration == UnlimitedServiceDuration { 233 logrus.Debug("APIServer.Shutdown ignored as Duration is UnlimitedService") 234 return nil 235 } 236 237 shutdownOnce.Do(func() { 238 if logrus.IsLevelEnabled(logrus.DebugLevel) { 239 _, file, line, _ := goRuntime.Caller(1) 240 logrus.Debugf("APIServer.Shutdown by %s:%d, %d/%d connection(s)", 241 file, line, s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections()) 242 243 go func() { 244 ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) 245 go func() { 246 defer cancel() 247 if err := s.pprof.Shutdown(ctx); err != nil { 248 logrus.Warn( 249 errors.Wrapf(err, "failed to cleanly shutdown pprof Server")) 250 } 251 }() 252 <-ctx.Done() 253 }() 254 } 255 256 // Gracefully shutdown server(s), duration of wait same as idle window 257 ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) 258 go func() { 259 defer cancel() 260 261 err := s.Server.Shutdown(ctx) 262 if err != nil && err != context.Canceled && err != http.ErrServerClosed { 263 logrus.Error( 264 errors.Wrapf(err, "failed to cleanly shutdown APIServer")) 265 } 266 }() 267 <-ctx.Done() 268 }) 269 270 return nil 271 } 272 273 // Close immediately stops responding to clients and exits 274 func (s *APIServer) Close() error { 275 return s.Server.Close() 276 }