github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/api/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"net"
     7  	"net/http"
     8  	"os"
     9  	"os/signal"
    10  	"runtime"
    11  	"strings"
    12  	"sync"
    13  	"syscall"
    14  	"time"
    15  
    16  	"github.com/containers/libpod/libpod"
    17  	"github.com/containers/libpod/pkg/api/handlers"
    18  	"github.com/coreos/go-systemd/v22/activation"
    19  	"github.com/gorilla/mux"
    20  	"github.com/gorilla/schema"
    21  	"github.com/pkg/errors"
    22  	"github.com/sirupsen/logrus"
    23  )
    24  
    25  type APIServer struct {
    26  	http.Server                     // The  HTTP work happens here
    27  	*schema.Decoder                 // Decoder for Query parameters to structs
    28  	context.Context                 // Context to carry objects to handlers
    29  	*libpod.Runtime                 // Where the real work happens
    30  	net.Listener                    // mux for routing HTTP API calls to libpod routines
    31  	context.CancelFunc              // Stop APIServer
    32  	idleTracker        *IdleTracker // Track connections to support idle shutdown
    33  }
    34  
    35  // Number of seconds to wait for next request, if exceeded shutdown server
    36  const (
    37  	DefaultServiceDuration   = 300 * time.Second
    38  	UnlimitedServiceDuration = 0 * time.Second
    39  )
    40  
    41  // NewServer will create and configure a new API server with all defaults
    42  func NewServer(runtime *libpod.Runtime) (*APIServer, error) {
    43  	return newServer(runtime, DefaultServiceDuration, nil)
    44  }
    45  
    46  // NewServerWithSettings will create and configure a new API server using provided settings
    47  func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) {
    48  	return newServer(runtime, duration, listener)
    49  }
    50  
    51  func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) {
    52  	// If listener not provided try socket activation protocol
    53  	if listener == nil {
    54  		if _, found := os.LookupEnv("LISTEN_FDS"); !found {
    55  			return nil, errors.Errorf("Cannot create API Server, no listener provided and socket activation protocol is not active.")
    56  		}
    57  
    58  		listeners, err := activation.Listeners()
    59  		if err != nil {
    60  			return nil, errors.Wrap(err, "Cannot retrieve file descriptors from systemd")
    61  		}
    62  		if len(listeners) != 1 {
    63  			return nil, errors.Errorf("Wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners))
    64  		}
    65  		listener = &listeners[0]
    66  	}
    67  
    68  	router := mux.NewRouter().UseEncodedPath()
    69  	idle := NewIdleTracker(duration)
    70  
    71  	server := APIServer{
    72  		Server: http.Server{
    73  			Handler:           router,
    74  			ReadHeaderTimeout: 20 * time.Second,
    75  			IdleTimeout:       duration,
    76  			ConnState:         idle.ConnState,
    77  			ErrorLog:          log.New(logrus.StandardLogger().Out, "", 0),
    78  		},
    79  		Decoder:     handlers.NewAPIDecoder(),
    80  		idleTracker: idle,
    81  		Listener:    *listener,
    82  		Runtime:     runtime,
    83  	}
    84  
    85  	router.NotFoundHandler = http.HandlerFunc(
    86  		func(w http.ResponseWriter, r *http.Request) {
    87  			// We can track user errors...
    88  			logrus.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusNotFound, http.StatusText(http.StatusNotFound), r.Method, r.URL.String())
    89  			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    90  		},
    91  	)
    92  
    93  	for _, fn := range []func(*mux.Router) error{
    94  		server.registerAuthHandlers,
    95  		server.registerContainersHandlers,
    96  		server.registerDistributionHandlers,
    97  		server.registerEventsHandlers,
    98  		server.registerExecHandlers,
    99  		server.registerHealthCheckHandlers,
   100  		server.registerImagesHandlers,
   101  		server.registerInfoHandlers,
   102  		server.registerManifestHandlers,
   103  		server.registerMonitorHandlers,
   104  		server.registerPingHandlers,
   105  		server.registerPluginsHandlers,
   106  		server.registerPodsHandlers,
   107  		server.RegisterSwaggerHandlers,
   108  		server.registerSwarmHandlers,
   109  		server.registerSystemHandlers,
   110  		server.registerVersionHandlers,
   111  		server.registerVolumeHandlers,
   112  	} {
   113  		if err := fn(router); err != nil {
   114  			return nil, err
   115  		}
   116  	}
   117  
   118  	if logrus.IsLevelEnabled(logrus.DebugLevel) {
   119  		router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint
   120  			path, err := route.GetPathTemplate()
   121  			if err != nil {
   122  				path = "<N/A>"
   123  			}
   124  			methods, err := route.GetMethods()
   125  			if err != nil {
   126  				methods = []string{"<N/A>"}
   127  			}
   128  			logrus.Debugf("Methods: %s Path: %s", strings.Join(methods, ", "), path)
   129  			return nil
   130  		})
   131  	}
   132  
   133  	return &server, nil
   134  }
   135  
   136  // Serve starts responding to HTTP requests
   137  func (s *APIServer) Serve() error {
   138  	sigChan := make(chan os.Signal, 1)
   139  	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
   140  	errChan := make(chan error, 1)
   141  
   142  	go func() {
   143  		<-s.idleTracker.Done()
   144  		logrus.Debugf("API Server idle for %v", s.idleTracker.Duration)
   145  		_ = s.Shutdown()
   146  	}()
   147  
   148  	go func() {
   149  		err := s.Server.Serve(s.Listener)
   150  		if err != nil && err != http.ErrServerClosed {
   151  			errChan <- errors.Wrap(err, "failed to start API server")
   152  			return
   153  		}
   154  		errChan <- nil
   155  	}()
   156  
   157  	select {
   158  	case err := <-errChan:
   159  		return err
   160  	case sig := <-sigChan:
   161  		logrus.Infof("APIServer terminated by signal %v", sig)
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  // Shutdown is a clean shutdown waiting on existing clients
   168  func (s *APIServer) Shutdown() error {
   169  	if logrus.IsLevelEnabled(logrus.DebugLevel) {
   170  		_, file, line, _ := runtime.Caller(1)
   171  		logrus.Debugf("APIServer.Shutdown by %s:%d, %d/%d connection(s)",
   172  			file, line, s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections())
   173  	}
   174  
   175  	// Duration == 0 flags no auto-shutdown of the server
   176  	if s.idleTracker.Duration == 0 {
   177  		logrus.Debug("APIServer.Shutdown ignored as Duration == 0")
   178  		return nil
   179  	}
   180  
   181  	// Gracefully shutdown server, duration of wait same as idle window
   182  	ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration)
   183  	defer cancel()
   184  	go func() {
   185  		err := s.Server.Shutdown(ctx)
   186  		if err != nil && err != context.Canceled && err != http.ErrServerClosed {
   187  			logrus.Errorf("Failed to cleanly shutdown APIServer: %s", err.Error())
   188  		}
   189  	}()
   190  	<-ctx.Done()
   191  	return nil
   192  }
   193  
   194  // Close immediately stops responding to clients and exits
   195  func (s *APIServer) Close() error {
   196  	return s.Server.Close()
   197  }
   198  
   199  type IdleTracker struct {
   200  	active   map[net.Conn]struct{}
   201  	total    int
   202  	mux      sync.Mutex
   203  	timer    *time.Timer
   204  	Duration time.Duration
   205  }
   206  
   207  func NewIdleTracker(idle time.Duration) *IdleTracker {
   208  	return &IdleTracker{
   209  		active:   make(map[net.Conn]struct{}),
   210  		Duration: idle,
   211  		timer:    time.NewTimer(idle),
   212  	}
   213  }
   214  
   215  func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) {
   216  	t.mux.Lock()
   217  	defer t.mux.Unlock()
   218  
   219  	oldActive := len(t.active)
   220  	logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, t.ActiveConnections(), t.TotalConnections())
   221  	switch state {
   222  	case http.StateNew, http.StateActive, http.StateHijacked:
   223  		t.active[conn] = struct{}{}
   224  		// stop the timer if we transitioned from idle
   225  		if oldActive == 0 {
   226  			t.timer.Stop()
   227  		}
   228  		t.total += 1
   229  	case http.StateIdle, http.StateClosed:
   230  		delete(t.active, conn)
   231  		// Restart the timer if we've become idle
   232  		if oldActive > 0 && len(t.active) == 0 {
   233  			t.timer.Stop()
   234  			t.timer.Reset(t.Duration)
   235  		}
   236  	}
   237  }
   238  
   239  func (t *IdleTracker) ActiveConnections() int {
   240  	return len(t.active)
   241  }
   242  
   243  func (t *IdleTracker) TotalConnections() int {
   244  	return t.total
   245  }
   246  
   247  func (t *IdleTracker) Done() <-chan time.Time {
   248  	return t.timer.C
   249  }