linkerd2/viz/metrics-api/http_server.go

248 lines
5.8 KiB
Go

package api
import (
"fmt"
"net/http"
"github.com/linkerd/linkerd2/controller/k8s"
"github.com/linkerd/linkerd2/pkg/prometheus"
"github.com/linkerd/linkerd2/pkg/protohttp"
"github.com/linkerd/linkerd2/viz/metrics-api/client"
pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
promApi "github.com/prometheus/client_golang/api"
promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
log "github.com/sirupsen/logrus"
)
var (
gatewaysPath = fullURLPathFor("Gateways")
statSummaryPath = fullURLPathFor("StatSummary")
topRoutesPath = fullURLPathFor("TopRoutes")
listPodsPath = fullURLPathFor("ListPods")
listServicesPath = fullURLPathFor("ListServices")
selfCheckPath = fullURLPathFor("SelfCheck")
edgesPath = fullURLPathFor("Edges")
)
type handler struct {
grpcServer Server
}
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.WithFields(log.Fields{
"req.Method": req.Method, "req.URL": req.URL, "req.Form": req.Form,
}).Debugf("Serving %s %s", req.Method, req.URL.Path)
// Validate request method
if req.Method != http.MethodPost {
protohttp.WriteErrorToHTTPResponse(w, fmt.Errorf("POST required"))
return
}
// Serve request
switch req.URL.Path {
case gatewaysPath:
h.handleGateways(w, req)
case statSummaryPath:
h.handleStatSummary(w, req)
case topRoutesPath:
h.handleTopRoutes(w, req)
case listPodsPath:
h.handleListPods(w, req)
case listServicesPath:
h.handleListServices(w, req)
case selfCheckPath:
h.handleSelfCheck(w, req)
case edgesPath:
h.handleEdges(w, req)
default:
http.NotFound(w, req)
}
}
func (h *handler) handleGateways(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.GatewaysRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.Gateways(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func (h *handler) handleStatSummary(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.StatSummaryRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.StatSummary(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func (h *handler) handleEdges(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.EdgesRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.Edges(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func (h *handler) handleTopRoutes(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.TopRoutesRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.TopRoutes(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func (h *handler) handleSelfCheck(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.SelfCheckRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.SelfCheck(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func (h *handler) handleListPods(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.ListPodsRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.ListPods(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func (h *handler) handleListServices(w http.ResponseWriter, req *http.Request) {
var protoRequest pb.ListServicesRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
rsp, err := h.grpcServer.ListServices(req.Context(), &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
err = protohttp.WriteProtoToHTTPResponse(w, rsp)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)
return
}
}
func fullURLPathFor(method string) string {
return client.APIRoot + client.APIPrefix + method
}
// NewServer creates a Public API HTTP server.
func NewServer(
addr string,
prometheusClient promApi.Client,
k8sAPI *k8s.API,
controllerNamespace string,
clusterDomain string,
ignoredNamespaces []string,
) *http.Server {
var promAPI promv1.API
if prometheusClient != nil {
promAPI = promv1.NewAPI(prometheusClient)
}
grpcServer := newGrpcServer(
promAPI,
k8sAPI,
controllerNamespace,
clusterDomain,
ignoredNamespaces,
)
baseHandler := &handler{
grpcServer: grpcServer,
}
instrumentedHandler := prometheus.WithTelemetry(baseHandler)
return &http.Server{
Addr: addr,
Handler: instrumentedHandler,
}
}