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  }