334 lines
10 KiB
Go
334 lines
10 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/golang/gddo/httputil"
|
|
"github.com/influxdata/platform"
|
|
pctx "github.com/influxdata/platform/context"
|
|
"github.com/influxdata/platform/kit/errors"
|
|
"github.com/julienschmidt/httprouter"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// TelegrafHandler is the handler for the telegraf service
|
|
type TelegrafHandler struct {
|
|
*httprouter.Router
|
|
Logger *zap.Logger
|
|
|
|
TelegrafService platform.TelegrafConfigStore
|
|
UserResourceMappingService platform.UserResourceMappingService
|
|
LabelService platform.LabelService
|
|
UserService platform.UserService
|
|
}
|
|
|
|
const (
|
|
telegrafsPath = "/api/v2/telegrafs"
|
|
telegrafsIDPath = "/api/v2/telegrafs/:id"
|
|
telegrafsIDMembersPath = "/api/v2/telegrafs/:id/members"
|
|
telegrafsIDMembersIDPath = "/api/v2/telegrafs/:id/members/:userID"
|
|
telegrafsIDOwnersPath = "/api/v2/telegrafs/:id/owners"
|
|
telegrafsIDOwnersIDPath = "/api/v2/telegrafs/:id/owners/:userID"
|
|
telegrafsIDLabelsPath = "/api/v2/telegrafs/:id/labels"
|
|
telegrafsIDLabelsNamePath = "/api/v2/telegrafs/:id/labels/:name"
|
|
)
|
|
|
|
// NewTelegrafHandler returns a new instance of TelegrafHandler.
|
|
func NewTelegrafHandler(
|
|
logger *zap.Logger,
|
|
mappingService platform.UserResourceMappingService,
|
|
labelService platform.LabelService,
|
|
telegrafSvc platform.TelegrafConfigStore,
|
|
userService platform.UserService,
|
|
) *TelegrafHandler {
|
|
h := &TelegrafHandler{
|
|
Router: NewRouter(),
|
|
|
|
UserResourceMappingService: mappingService,
|
|
LabelService: labelService,
|
|
TelegrafService: telegrafSvc,
|
|
Logger: logger,
|
|
UserService: userService,
|
|
}
|
|
h.HandlerFunc("POST", telegrafsPath, h.handlePostTelegraf)
|
|
h.HandlerFunc("GET", telegrafsPath, h.handleGetTelegrafs)
|
|
h.HandlerFunc("GET", telegrafsIDPath, h.handleGetTelegraf)
|
|
h.HandlerFunc("DELETE", telegrafsIDPath, h.handleDeleteTelegraf)
|
|
h.HandlerFunc("PUT", telegrafsIDPath, h.handlePutTelegraf)
|
|
|
|
h.HandlerFunc("POST", telegrafsIDMembersPath, newPostMemberHandler(h.UserResourceMappingService, h.UserService, platform.TelegrafsResource, platform.Member))
|
|
h.HandlerFunc("GET", telegrafsIDMembersPath, newGetMembersHandler(h.UserResourceMappingService, h.UserService, platform.TelegrafsResource, platform.Member))
|
|
h.HandlerFunc("DELETE", telegrafsIDMembersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Member))
|
|
|
|
h.HandlerFunc("POST", telegrafsIDOwnersPath, newPostMemberHandler(h.UserResourceMappingService, h.UserService, platform.TelegrafsResource, platform.Owner))
|
|
h.HandlerFunc("GET", telegrafsIDOwnersPath, newGetMembersHandler(h.UserResourceMappingService, h.UserService, platform.TelegrafsResource, platform.Owner))
|
|
h.HandlerFunc("DELETE", telegrafsIDOwnersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Owner))
|
|
|
|
h.HandlerFunc("GET", telegrafsIDLabelsPath, newGetLabelsHandler(h.LabelService))
|
|
h.HandlerFunc("POST", telegrafsIDLabelsPath, newPostLabelHandler(h.LabelService))
|
|
h.HandlerFunc("DELETE", telegrafsIDLabelsNamePath, newDeleteLabelHandler(h.LabelService))
|
|
h.HandlerFunc("PATCH", telegrafsIDLabelsNamePath, newPatchLabelHandler(h.LabelService))
|
|
|
|
return h
|
|
}
|
|
|
|
type telegrafLinks struct {
|
|
Self string `json:"self"`
|
|
Labels string `json:"labels"`
|
|
}
|
|
|
|
type telegrafResponse struct {
|
|
*platform.TelegrafConfig
|
|
Labels []platform.Label `json:"labels"`
|
|
Links telegrafLinks `json:"links"`
|
|
}
|
|
|
|
type telegrafResponses struct {
|
|
TelegrafConfigs []telegrafResponse `json:"configurations"`
|
|
}
|
|
|
|
func newTelegrafResponse(tc *platform.TelegrafConfig, labels []*platform.Label) telegrafResponse {
|
|
res := telegrafResponse{
|
|
TelegrafConfig: tc,
|
|
Links: telegrafLinks{
|
|
Self: fmt.Sprintf("/api/v2/telegrafs/%s", tc.ID),
|
|
Labels: fmt.Sprintf("/api/v2/telegrafs/%s/labels", tc.ID),
|
|
},
|
|
Labels: []platform.Label{},
|
|
}
|
|
|
|
for _, l := range labels {
|
|
res.Labels = append(res.Labels, *l)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func newTelegrafResponses(ctx context.Context, tcs []*platform.TelegrafConfig, labelService platform.LabelService) telegrafResponses {
|
|
resp := telegrafResponses{
|
|
TelegrafConfigs: make([]telegrafResponse, len(tcs)),
|
|
}
|
|
for i, c := range tcs {
|
|
labels, _ := labelService.FindLabels(ctx, platform.LabelFilter{ResourceID: c.ID})
|
|
resp.TelegrafConfigs[i] = newTelegrafResponse(c, labels)
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func decodeGetTelegrafRequest(ctx context.Context, r *http.Request) (i platform.ID, err error) {
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
id := params.ByName("id")
|
|
if id == "" {
|
|
return i, errors.InvalidDataf("url missing id")
|
|
}
|
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
return i, err
|
|
}
|
|
return i, nil
|
|
}
|
|
|
|
func (h *TelegrafHandler) handleGetTelegrafs(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
filter, err := decodeTelegrafConfigFilter(ctx, r)
|
|
if err != nil {
|
|
h.Logger.Debug("failed to decode request", zap.Error(err))
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
tcs, _, err := h.TelegrafService.FindTelegrafConfigs(ctx, *filter)
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponses(ctx, tcs, h.LabelService)); err != nil {
|
|
logEncodingError(h.Logger, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, err := decodeGetTelegrafRequest(ctx, r)
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
tc, err := h.TelegrafService.FindTelegrafConfigByID(ctx, id)
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
offers := []string{"application/toml", "application/json", "application/octet-stream"}
|
|
defaultOffer := "application/toml"
|
|
mimeType := httputil.NegotiateContentType(r, offers, defaultOffer)
|
|
switch mimeType {
|
|
case "application/octet-stream":
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.toml\"", strings.Replace(strings.TrimSpace(tc.Name), " ", "_", -1)))
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(tc.TOML()))
|
|
case "application/json":
|
|
labels, err := h.LabelService.FindLabels(ctx, platform.LabelFilter{ResourceID: tc.ID})
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc, labels)); err != nil {
|
|
logEncodingError(h.Logger, r, err)
|
|
return
|
|
}
|
|
case "application/toml":
|
|
w.Header().Set("Content-Type", "application/toml; charset=utf-8")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(tc.TOML()))
|
|
}
|
|
}
|
|
|
|
func decodeTelegrafConfigFilter(ctx context.Context, r *http.Request) (*platform.TelegrafConfigFilter, error) {
|
|
f := &platform.TelegrafConfigFilter{}
|
|
urm, err := decodeUserResourceMappingFilter(ctx, r)
|
|
if err != nil {
|
|
f.UserResourceMappingFilter = *urm
|
|
}
|
|
|
|
q := r.URL.Query()
|
|
if orgIDStr := q.Get("orgID"); orgIDStr != "" {
|
|
orgID, _ := platform.IDFromString(orgIDStr)
|
|
f.OrganizationID = orgID
|
|
}
|
|
|
|
return f, err
|
|
}
|
|
|
|
func decodeUserResourceMappingFilter(ctx context.Context, r *http.Request) (*platform.UserResourceMappingFilter, error) {
|
|
q := r.URL.Query()
|
|
f := &platform.UserResourceMappingFilter{
|
|
Resource: platform.TelegrafsResource,
|
|
}
|
|
if idStr := q.Get("resourceId"); idStr != "" {
|
|
id, err := platform.IDFromString(idStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f.ResourceID = *id
|
|
}
|
|
|
|
if idStr := q.Get("userId"); idStr != "" {
|
|
id, err := platform.IDFromString(idStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f.UserID = *id
|
|
}
|
|
return f, nil
|
|
}
|
|
|
|
func decodePostTelegrafRequest(ctx context.Context, r *http.Request) (*platform.TelegrafConfig, error) {
|
|
tc := new(platform.TelegrafConfig)
|
|
err := json.NewDecoder(r.Body).Decode(tc)
|
|
return tc, err
|
|
}
|
|
|
|
func decodePutTelegrafRequest(ctx context.Context, r *http.Request) (*platform.TelegrafConfig, error) {
|
|
tc := new(platform.TelegrafConfig)
|
|
if err := json.NewDecoder(r.Body).Decode(tc); err != nil {
|
|
return nil, err
|
|
}
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
id := params.ByName("id")
|
|
if id == "" {
|
|
return nil, errors.InvalidDataf("url missing id")
|
|
}
|
|
i := new(platform.ID)
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
return nil, err
|
|
}
|
|
tc.ID = *i
|
|
return tc, nil
|
|
}
|
|
|
|
// handlePostTelegraf is the HTTP handler for the POST /api/v2/telegrafs route.
|
|
func (h *TelegrafHandler) handlePostTelegraf(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
tc, err := decodePostTelegrafRequest(ctx, r)
|
|
if err != nil {
|
|
h.Logger.Debug("failed to decode request", zap.Error(err))
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
auth, err := pctx.GetAuthorizer(ctx)
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := h.TelegrafService.CreateTelegrafConfig(ctx, tc, auth.GetUserID()); err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusCreated, newTelegrafResponse(tc, []*platform.Label{})); err != nil {
|
|
logEncodingError(h.Logger, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// handlePutTelegraf is the HTTP handler for the POST /api/v2/telegrafs route.
|
|
func (h *TelegrafHandler) handlePutTelegraf(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
tc, err := decodePutTelegrafRequest(ctx, r)
|
|
if err != nil {
|
|
h.Logger.Debug("failed to decode request", zap.Error(err))
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
auth, err := pctx.GetAuthorizer(ctx)
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
tc, err = h.TelegrafService.UpdateTelegrafConfig(ctx, tc.ID, tc, auth.GetUserID())
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
labels, err := h.LabelService.FindLabels(ctx, platform.LabelFilter{ResourceID: tc.ID})
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc, labels)); err != nil {
|
|
logEncodingError(h.Logger, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (h *TelegrafHandler) handleDeleteTelegraf(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
i, err := decodeGetTelegrafRequest(ctx, r)
|
|
if err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err = h.TelegrafService.DeleteTelegrafConfig(ctx, i); err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusNoContent, nil); err != nil {
|
|
logEncodingError(h.Logger, r, err)
|
|
return
|
|
}
|
|
}
|