mirror of https://github.com/linkerd/linkerd2.git
161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
package srv
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/golang/protobuf/jsonpb"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/julienschmidt/httprouter"
|
|
"github.com/runconduit/conduit/controller/api/util"
|
|
pb "github.com/runconduit/conduit/controller/gen/public"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type (
|
|
jsonError struct {
|
|
Error string `json:"error"`
|
|
}
|
|
)
|
|
|
|
var (
|
|
defaultMetricTimeWindow = pb.TimeWindow_ONE_MIN
|
|
defaultMetricAggregationType = pb.AggregationType_TARGET_DEPLOY
|
|
|
|
allMetrics = []pb.MetricName{
|
|
pb.MetricName_REQUEST_RATE,
|
|
pb.MetricName_SUCCESS_RATE,
|
|
pb.MetricName_LATENCY,
|
|
}
|
|
|
|
meshMetrics = []pb.MetricName{
|
|
pb.MetricName_REQUEST_RATE,
|
|
pb.MetricName_SUCCESS_RATE,
|
|
}
|
|
|
|
pbMarshaler = jsonpb.Marshaler{EmitDefaults: true}
|
|
)
|
|
|
|
func renderJsonError(w http.ResponseWriter, err error, status int) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
log.Error(err.Error())
|
|
rsp, _ := json.Marshal(jsonError{Error: err.Error()})
|
|
w.WriteHeader(status)
|
|
w.Write(rsp)
|
|
}
|
|
|
|
func renderJson(w http.ResponseWriter, resp interface{}) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
jsonResp, err := json.Marshal(resp)
|
|
if err != nil {
|
|
renderJsonError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Write(jsonResp)
|
|
}
|
|
|
|
func renderJsonPb(w http.ResponseWriter, msg proto.Message) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
pbMarshaler.Marshal(w, msg)
|
|
}
|
|
|
|
func (h *handler) handleApiVersion(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
|
version, err := h.apiClient.Version(req.Context(), &pb.Empty{})
|
|
|
|
if err != nil {
|
|
renderJsonError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
resp := map[string]interface{}{
|
|
"version": version,
|
|
}
|
|
renderJson(w, resp)
|
|
}
|
|
|
|
func validateMetricParams(metricNameParam, aggParam, timeWindowParam string) (
|
|
metrics []pb.MetricName,
|
|
groupBy pb.AggregationType,
|
|
window pb.TimeWindow,
|
|
err error,
|
|
) {
|
|
groupBy = defaultMetricAggregationType
|
|
if aggParam != "" {
|
|
groupBy, err = util.GetAggregationType(aggParam)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
metrics = allMetrics
|
|
if metricNameParam != "" {
|
|
var requestedMetricName pb.MetricName
|
|
requestedMetricName, err = util.GetMetricName(metricNameParam)
|
|
if err != nil {
|
|
return
|
|
}
|
|
metrics = []pb.MetricName{requestedMetricName}
|
|
} else if groupBy == pb.AggregationType_MESH {
|
|
metrics = meshMetrics
|
|
}
|
|
|
|
window = defaultMetricTimeWindow
|
|
if timeWindowParam != "" {
|
|
var requestedWindow pb.TimeWindow
|
|
requestedWindow, err = util.GetWindow(timeWindowParam)
|
|
if err != nil {
|
|
return
|
|
}
|
|
window = requestedWindow
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (h *handler) handleApiMetrics(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
|
metricNameParam := req.FormValue("metric")
|
|
timeWindowParam := req.FormValue("window")
|
|
aggParam := req.FormValue("aggregation")
|
|
timeseries := req.FormValue("timeseries") == "true"
|
|
|
|
filterBy := pb.MetricMetadata{
|
|
TargetPod: req.FormValue("target_pod"),
|
|
TargetDeploy: req.FormValue("target_deploy"),
|
|
SourcePod: req.FormValue("source_pod"),
|
|
SourceDeploy: req.FormValue("source_deploy"),
|
|
Component: req.FormValue("component"),
|
|
Path: req.FormValue("path"),
|
|
}
|
|
|
|
metrics, groupBy, window, err := validateMetricParams(metricNameParam, aggParam, timeWindowParam)
|
|
if err != nil {
|
|
renderJsonError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
metricsRequest := &pb.MetricRequest{
|
|
Metrics: metrics,
|
|
Window: window,
|
|
FilterBy: &filterBy,
|
|
GroupBy: groupBy,
|
|
Summarize: !timeseries,
|
|
}
|
|
|
|
metricsResponse, err := h.apiClient.Stat(req.Context(), metricsRequest)
|
|
if err != nil {
|
|
renderJsonError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
renderJsonPb(w, metricsResponse)
|
|
}
|
|
|
|
func (h *handler) handleApiPods(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
|
pods, err := h.apiClient.ListPods(req.Context(), &pb.Empty{})
|
|
if err != nil {
|
|
renderJsonError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
renderJsonPb(w, pods)
|
|
}
|