...

Source file src/github.com/emissary-ingress/emissary/v3/cmd/entrypoint/healthcheck_server.go

Documentation: github.com/emissary-ingress/emissary/v3/cmd/entrypoint

     1  package entrypoint
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/http"
     7  	"net/http/httputil"
     8  	"net/http/pprof"
     9  	"net/url"
    10  
    11  	_ "k8s.io/client-go/plugin/pkg/client/auth"
    12  	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
    13  
    14  	"github.com/datawire/dlib/dhttp"
    15  	"github.com/emissary-ingress/emissary/v3/pkg/acp"
    16  	"github.com/emissary-ingress/emissary/v3/pkg/debug"
    17  )
    18  
    19  func handleCheckAlive(w http.ResponseWriter, r *http.Request, ambwatch *acp.AmbassadorWatcher) {
    20  	// The liveness check needs to explicitly try to talk to Envoy...
    21  	ambwatch.FetchEnvoyReady(r.Context())
    22  
    23  	// ...then check if the watcher says we're alive.
    24  	ok := ambwatch.IsAlive()
    25  
    26  	if ok {
    27  		_, _ = w.Write([]byte("Ambassador is alive and well\n"))
    28  	} else {
    29  		http.Error(w, "Ambassador is not alive\n", http.StatusServiceUnavailable)
    30  	}
    31  }
    32  
    33  func handleCheckReady(w http.ResponseWriter, r *http.Request, ambwatch *acp.AmbassadorWatcher) {
    34  	// The readiness check needs to explicitly try to talk to Envoy, too. Why?
    35  	// Because if you have a pod configured with only the readiness check but
    36  	// not the liveness check, and we don't try to talk to Envoy here, then we
    37  	// will never ever attempt to talk to Envoy at all, Envoy will never be
    38  	// declared alive, and we'll never consider Ambassador ready.
    39  	ambwatch.FetchEnvoyReady(r.Context())
    40  
    41  	ok := ambwatch.IsReady()
    42  
    43  	if ok {
    44  		_, _ = w.Write([]byte("Ambassador is ready and waiting\n"))
    45  	} else {
    46  		http.Error(w, "Ambassador is not ready\n", http.StatusServiceUnavailable)
    47  	}
    48  }
    49  
    50  func healthCheckHandler(ctx context.Context, ambwatch *acp.AmbassadorWatcher) error {
    51  	dbg := debug.FromContext(ctx)
    52  
    53  	// We need to do some HTTP stuff by hand to catch the readiness and liveness
    54  	// checks here, but forward everything else to diagd.
    55  	sm := http.NewServeMux()
    56  
    57  	// Handle the liveness check and the readiness check directly, by handing them
    58  	// off to our functions.
    59  
    60  	livenessTimer := dbg.Timer("check_alive")
    61  	sm.HandleFunc("/ambassador/v0/check_alive",
    62  		livenessTimer.TimedHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    63  			handleCheckAlive(w, r, ambwatch)
    64  		}))
    65  
    66  	readinessTimer := dbg.Timer("check_ready")
    67  	sm.HandleFunc("/ambassador/v0/check_ready",
    68  		readinessTimer.TimedHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    69  			handleCheckReady(w, r, ambwatch)
    70  		}))
    71  
    72  	// Serve any debug info from the golang codebase.
    73  	sm.Handle("/debug", dbg)
    74  
    75  	// Serve pprof endpoints to aid in live debugging.
    76  	sm.HandleFunc("/debug/pprof/", pprof.Index)
    77  	sm.HandleFunc("/debug/pprof/profile", pprof.Profile)
    78  	sm.HandleFunc("/debug/pprof/trace", pprof.Trace)
    79  	sm.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
    80  	sm.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    81  
    82  	// For everything else, use a ReverseProxy to forward it to diagd.
    83  	//
    84  	// diagdOrigin is where diagd is listening.
    85  	diagdOrigin, _ := url.Parse("http://127.0.0.1:8004/")
    86  
    87  	// This reverseProxy is dirt simple: use a director function to
    88  	// swap the scheme and host of our request for the ones from the
    89  	// diagdOrigin. Leave everything else (notably including the path)
    90  	// alone.
    91  	reverseProxy := &httputil.ReverseProxy{
    92  		Director: func(req *http.Request) {
    93  			req.URL.Scheme = diagdOrigin.Scheme
    94  			req.URL.Host = diagdOrigin.Host
    95  
    96  			// If this request is coming from localhost, tell diagd about that.
    97  			if acp.HostPortIsLocal(req.RemoteAddr) {
    98  				req.Header.Set("X-Ambassador-Diag-IP", "127.0.0.1")
    99  			}
   100  		},
   101  	}
   102  
   103  	// Finally, use the reverseProxy to handle anything coming in on
   104  	// the magic catchall path.
   105  	sm.HandleFunc("/", reverseProxy.ServeHTTP)
   106  
   107  	// Set up listener.
   108  	// The default value for network is ANY.
   109  	// It means in case of any wildcard address, the listener will try to listen on both IPv4 and IPv6
   110  	// If you want to specify AF explicitly,
   111  	// you can set the AMBASSADOR_HEALTHCHECK_IP_FAMILY environment variable to IPV4_ONLY or IPV6_ONLY respectively
   112  	addr := net.JoinHostPort(getHealthCheckHost(), getHealthCheckPort())
   113  	network := getHealthCheckIPNetworkFamily()
   114  	listener, err := net.Listen(network, addr)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	s := &dhttp.ServerConfig{
   120  		Handler: sm,
   121  	}
   122  
   123  	return s.Serve(ctx, listener)
   124  }
   125  

View as plain text