2018-10-24 15:13:30 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
2019-12-04 01:00:15 +00:00
|
|
|
"bytes"
|
2018-10-24 15:13:30 +00:00
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2019-12-04 01:00:15 +00:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2018-10-24 15:13:30 +00:00
|
|
|
"net/http"
|
2019-12-04 01:00:15 +00:00
|
|
|
"path"
|
2018-10-31 18:45:28 +00:00
|
|
|
"strings"
|
2018-10-24 15:13:30 +00:00
|
|
|
|
2018-12-21 15:48:40 +00:00
|
|
|
"github.com/golang/gddo/httputil"
|
2019-11-25 14:22:19 +00:00
|
|
|
"github.com/influxdata/httprouter"
|
2019-01-08 00:37:16 +00:00
|
|
|
platform "github.com/influxdata/influxdb"
|
|
|
|
pctx "github.com/influxdata/influxdb/context"
|
2019-03-08 18:14:22 +00:00
|
|
|
"github.com/influxdata/influxdb/telegraf/plugins"
|
2018-10-31 18:10:03 +00:00
|
|
|
"go.uber.org/zap"
|
2018-10-24 15:13:30 +00:00
|
|
|
)
|
|
|
|
|
2019-01-16 15:48:17 +00:00
|
|
|
// TelegrafBackend is all services and associated parameters required to construct
|
|
|
|
// the TelegrafHandler.
|
|
|
|
type TelegrafBackend struct {
|
2019-06-27 01:33:20 +00:00
|
|
|
platform.HTTPErrorHandler
|
2019-01-16 15:48:17 +00:00
|
|
|
Logger *zap.Logger
|
|
|
|
|
|
|
|
TelegrafService platform.TelegrafConfigStore
|
|
|
|
UserResourceMappingService platform.UserResourceMappingService
|
|
|
|
LabelService platform.LabelService
|
|
|
|
UserService platform.UserService
|
|
|
|
OrganizationService platform.OrganizationService
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTelegrafBackend returns a new instance of TelegrafBackend.
|
|
|
|
func NewTelegrafBackend(b *APIBackend) *TelegrafBackend {
|
|
|
|
return &TelegrafBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
|
|
|
Logger: b.Logger.With(zap.String("handler", "telegraf")),
|
2019-01-16 15:48:17 +00:00
|
|
|
|
|
|
|
TelegrafService: b.TelegrafService,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
OrganizationService: b.OrganizationService,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-24 15:13:30 +00:00
|
|
|
// TelegrafHandler is the handler for the telegraf service
|
|
|
|
type TelegrafHandler struct {
|
|
|
|
*httprouter.Router
|
2019-06-27 01:33:20 +00:00
|
|
|
platform.HTTPErrorHandler
|
2018-10-31 18:10:03 +00:00
|
|
|
Logger *zap.Logger
|
2018-10-24 15:13:30 +00:00
|
|
|
|
|
|
|
TelegrafService platform.TelegrafConfigStore
|
|
|
|
UserResourceMappingService platform.UserResourceMappingService
|
2018-12-11 18:15:34 +00:00
|
|
|
LabelService platform.LabelService
|
2018-11-17 15:54:21 +00:00
|
|
|
UserService platform.UserService
|
2019-01-14 17:07:51 +00:00
|
|
|
OrganizationService platform.OrganizationService
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2019-01-18 19:03:36 +00:00
|
|
|
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"
|
|
|
|
telegrafsIDLabelsIDPath = "/api/v2/telegrafs/:id/labels/:lid"
|
2018-10-24 15:13:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// NewTelegrafHandler returns a new instance of TelegrafHandler.
|
2019-01-16 15:48:17 +00:00
|
|
|
func NewTelegrafHandler(b *TelegrafBackend) *TelegrafHandler {
|
2018-10-24 15:13:30 +00:00
|
|
|
h := &TelegrafHandler{
|
2019-06-27 01:33:20 +00:00
|
|
|
Router: NewRouter(b.HTTPErrorHandler),
|
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
|
|
|
Logger: b.Logger,
|
2018-11-01 18:06:35 +00:00
|
|
|
|
2019-01-16 15:48:17 +00:00
|
|
|
TelegrafService: b.TelegrafService,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
OrganizationService: b.OrganizationService,
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
|
2019-01-17 03:35:54 +00:00
|
|
|
memberBackend := MemberBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
2019-01-17 03:35:54 +00:00
|
|
|
Logger: b.Logger.With(zap.String("handler", "member")),
|
|
|
|
ResourceType: platform.TelegrafsResourceType,
|
|
|
|
UserType: platform.Member,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("POST", telegrafsIDMembersPath, newPostMemberHandler(memberBackend))
|
|
|
|
h.HandlerFunc("GET", telegrafsIDMembersPath, newGetMembersHandler(memberBackend))
|
|
|
|
h.HandlerFunc("DELETE", telegrafsIDMembersIDPath, newDeleteMemberHandler(memberBackend))
|
|
|
|
|
|
|
|
ownerBackend := MemberBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
2019-01-17 03:35:54 +00:00
|
|
|
Logger: b.Logger.With(zap.String("handler", "member")),
|
|
|
|
ResourceType: platform.TelegrafsResourceType,
|
|
|
|
UserType: platform.Owner,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("POST", telegrafsIDOwnersPath, newPostMemberHandler(ownerBackend))
|
|
|
|
h.HandlerFunc("GET", telegrafsIDOwnersPath, newGetMembersHandler(ownerBackend))
|
|
|
|
h.HandlerFunc("DELETE", telegrafsIDOwnersIDPath, newDeleteMemberHandler(ownerBackend))
|
2018-10-24 15:13:30 +00:00
|
|
|
|
2019-01-17 04:33:09 +00:00
|
|
|
labelBackend := &LabelBackend{
|
2019-06-27 01:33:20 +00:00
|
|
|
HTTPErrorHandler: b.HTTPErrorHandler,
|
|
|
|
Logger: b.Logger.With(zap.String("handler", "label")),
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
ResourceType: platform.TelegrafsResourceType,
|
2019-01-17 04:33:09 +00:00
|
|
|
}
|
|
|
|
h.HandlerFunc("GET", telegrafsIDLabelsPath, newGetLabelsHandler(labelBackend))
|
|
|
|
h.HandlerFunc("POST", telegrafsIDLabelsPath, newPostLabelHandler(labelBackend))
|
|
|
|
h.HandlerFunc("DELETE", telegrafsIDLabelsIDPath, newDeleteLabelHandler(labelBackend))
|
2018-12-11 18:15:34 +00:00
|
|
|
|
2018-10-24 15:13:30 +00:00
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2019-01-03 19:48:26 +00:00
|
|
|
type telegrafLinks struct {
|
2019-02-26 17:08:30 +00:00
|
|
|
Self string `json:"self"`
|
|
|
|
Labels string `json:"labels"`
|
|
|
|
Members string `json:"members"`
|
|
|
|
Owners string `json:"owners"`
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
2019-03-08 18:14:22 +00:00
|
|
|
// MarshalJSON implement the json.Marshaler interface.
|
|
|
|
// TODO: remove this hack and make labels and links return.
|
|
|
|
// see: https://github.com/influxdata/influxdb/issues/12457
|
|
|
|
func (r *telegrafResponse) MarshalJSON() ([]byte, error) {
|
|
|
|
// telegrafPluginEncode is the helper struct for json encoding.
|
|
|
|
type telegrafPluginEncode struct {
|
|
|
|
// Name of the telegraf plugin, exp "docker"
|
|
|
|
Name string `json:"name"`
|
|
|
|
Type plugins.Type `json:"type"`
|
|
|
|
Comment string `json:"comment"`
|
|
|
|
Config plugins.Config `json:"config"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// telegrafConfigEncode is the helper struct for json encoding.
|
|
|
|
type telegrafConfigEncode struct {
|
2019-04-15 19:25:48 +00:00
|
|
|
ID platform.ID `json:"id"`
|
2019-06-06 14:12:32 +00:00
|
|
|
OrgID platform.ID `json:"orgID,omitempty"`
|
2019-04-15 19:25:48 +00:00
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Agent platform.TelegrafAgentConfig `json:"agent"`
|
|
|
|
Plugins []telegrafPluginEncode `json:"plugins"`
|
|
|
|
Labels []platform.Label `json:"labels"`
|
|
|
|
Links telegrafLinks `json:"links"`
|
2019-03-08 18:14:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tce := new(telegrafConfigEncode)
|
|
|
|
*tce = telegrafConfigEncode{
|
2019-04-15 19:25:48 +00:00
|
|
|
ID: r.ID,
|
|
|
|
OrgID: r.OrgID,
|
|
|
|
Name: r.Name,
|
|
|
|
Description: r.Description,
|
|
|
|
Agent: r.Agent,
|
|
|
|
Plugins: make([]telegrafPluginEncode, len(r.Plugins)),
|
|
|
|
Labels: r.Labels,
|
|
|
|
Links: r.Links,
|
2019-03-08 18:14:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for k, p := range r.Plugins {
|
|
|
|
tce.Plugins[k] = telegrafPluginEncode{
|
|
|
|
Name: p.Config.PluginName(),
|
|
|
|
Type: p.Config.Type(),
|
|
|
|
Comment: p.Comment,
|
|
|
|
Config: p.Config,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return json.Marshal(tce)
|
|
|
|
}
|
|
|
|
|
2018-10-24 15:13:30 +00:00
|
|
|
type telegrafResponse struct {
|
|
|
|
*platform.TelegrafConfig
|
2019-01-03 19:48:26 +00:00
|
|
|
Labels []platform.Label `json:"labels"`
|
|
|
|
Links telegrafLinks `json:"links"`
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type telegrafResponses struct {
|
2019-03-08 21:12:46 +00:00
|
|
|
TelegrafConfigs []*telegrafResponse `json:"configurations"`
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
2019-03-08 21:12:46 +00:00
|
|
|
func newTelegrafResponse(tc *platform.TelegrafConfig, labels []*platform.Label) *telegrafResponse {
|
|
|
|
res := &telegrafResponse{
|
2018-10-24 15:13:30 +00:00
|
|
|
TelegrafConfig: tc,
|
2019-01-03 19:48:26 +00:00
|
|
|
Links: telegrafLinks{
|
2019-02-26 17:08:30 +00:00
|
|
|
Self: fmt.Sprintf("/api/v2/telegrafs/%s", tc.ID),
|
|
|
|
Labels: fmt.Sprintf("/api/v2/telegrafs/%s/labels", tc.ID),
|
|
|
|
Members: fmt.Sprintf("/api/v2/telegrafs/%s/members", tc.ID),
|
|
|
|
Owners: fmt.Sprintf("/api/v2/telegrafs/%s/owners", tc.ID),
|
2018-10-24 15:13:30 +00:00
|
|
|
},
|
2019-01-03 23:56:52 +00:00
|
|
|
Labels: []platform.Label{},
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
2019-01-03 19:48:26 +00:00
|
|
|
|
|
|
|
for _, l := range labels {
|
|
|
|
res.Labels = append(res.Labels, *l)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
2019-03-08 21:12:46 +00:00
|
|
|
func newTelegrafResponses(ctx context.Context, tcs []*platform.TelegrafConfig, labelService platform.LabelService) *telegrafResponses {
|
|
|
|
resp := &telegrafResponses{
|
|
|
|
TelegrafConfigs: make([]*telegrafResponse, len(tcs)),
|
2018-10-30 20:09:28 +00:00
|
|
|
}
|
|
|
|
for i, c := range tcs {
|
2019-01-18 19:03:36 +00:00
|
|
|
labels, _ := labelService.FindResourceLabels(ctx, platform.LabelMappingFilter{ResourceID: c.ID})
|
2019-01-03 19:48:26 +00:00
|
|
|
resp.TelegrafConfigs[i] = newTelegrafResponse(c, labels)
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
2018-10-30 20:09:28 +00:00
|
|
|
return resp
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 01:00:15 +00:00
|
|
|
func decodeGetTelegrafRequest(ctx context.Context) (i platform.ID, err error) {
|
2018-10-24 15:13:30 +00:00
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-01-24 00:15:42 +00:00
|
|
|
return i, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2019-01-09 19:14:38 +00:00
|
|
|
filter, err := decodeTelegrafConfigFilter(ctx, r)
|
2018-10-24 15:13:30 +00:00
|
|
|
if err != nil {
|
2018-10-31 18:10:03 +00:00
|
|
|
h.Logger.Debug("failed to decode request", zap.Error(err))
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
tcs, _, err := h.TelegrafService.FindTelegrafConfigs(ctx, *filter)
|
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
2019-07-09 15:16:26 +00:00
|
|
|
h.Logger.Debug("telegrafs retrieved", zap.String("telegrafs", fmt.Sprint(tcs)))
|
|
|
|
|
2019-01-03 19:48:26 +00:00
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponses(ctx, tcs, h.LabelService)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
2019-12-04 01:00:15 +00:00
|
|
|
id, err := decodeGetTelegrafRequest(ctx)
|
2018-10-24 15:13:30 +00:00
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
tc, err := h.TelegrafService.FindTelegrafConfigByID(ctx, id)
|
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
2019-07-09 15:16:26 +00:00
|
|
|
h.Logger.Debug("telegraf retrieved", zap.String("telegraf", fmt.Sprint(tc)))
|
2018-10-24 15:13:30 +00:00
|
|
|
|
2018-12-21 15:48:40 +00:00
|
|
|
offers := []string{"application/toml", "application/json", "application/octet-stream"}
|
|
|
|
defaultOffer := "application/toml"
|
|
|
|
mimeType := httputil.NegotiateContentType(r, offers, defaultOffer)
|
2018-10-31 16:00:52 +00:00
|
|
|
switch mimeType {
|
2018-10-24 15:13:30 +00:00
|
|
|
case "application/octet-stream":
|
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
2018-10-31 18:45:28 +00:00
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.toml\"", strings.Replace(strings.TrimSpace(tc.Name), " ", "_", -1)))
|
2018-10-24 15:13:30 +00:00
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte(tc.TOML()))
|
|
|
|
case "application/json":
|
2019-01-18 19:03:36 +00:00
|
|
|
labels, err := h.LabelService.FindResourceLabels(ctx, platform.LabelMappingFilter{ResourceID: tc.ID})
|
2019-01-03 19:48:26 +00:00
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2019-01-03 19:48:26 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc, labels)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-21 15:48:40 +00:00
|
|
|
case "application/toml":
|
2018-10-31 16:00:52 +00:00
|
|
|
w.Header().Set("Content-Type", "application/toml; charset=utf-8")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte(tc.TOML()))
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-09 19:14:38 +00:00
|
|
|
func decodeTelegrafConfigFilter(ctx context.Context, r *http.Request) (*platform.TelegrafConfigFilter, error) {
|
|
|
|
f := &platform.TelegrafConfigFilter{}
|
2019-07-24 21:53:53 +00:00
|
|
|
urm, err := decodeUserResourceMappingFilter(ctx, r, platform.TelegrafsResourceType)
|
2019-01-14 17:07:51 +00:00
|
|
|
if err == nil {
|
2019-01-09 21:01:37 +00:00
|
|
|
f.UserResourceMappingFilter = *urm
|
2019-01-09 19:14:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
q := r.URL.Query()
|
|
|
|
if orgIDStr := q.Get("orgID"); orgIDStr != "" {
|
2019-01-14 17:07:51 +00:00
|
|
|
orgID, err := platform.IDFromString(orgIDStr)
|
|
|
|
if err != nil {
|
|
|
|
return f, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "orgID is invalid",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
2019-04-15 19:25:48 +00:00
|
|
|
f.OrgID = orgID
|
2019-01-14 17:07:51 +00:00
|
|
|
} else if orgNameStr := q.Get("org"); orgNameStr != "" {
|
|
|
|
*f.Organization = orgNameStr
|
2019-01-09 19:14:38 +00:00
|
|
|
}
|
|
|
|
return f, err
|
|
|
|
}
|
|
|
|
|
2019-12-04 01:00:15 +00:00
|
|
|
func decodePostTelegrafRequest(r *http.Request) (*platform.TelegrafConfig, error) {
|
|
|
|
var tc platform.TelegrafConfig
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&tc)
|
|
|
|
return &tc, err
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 == "" {
|
2019-01-24 00:15:42 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
|
|
|
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()
|
2019-12-04 01:00:15 +00:00
|
|
|
tc, err := decodePostTelegrafRequest(r)
|
2018-10-24 15:13:30 +00:00
|
|
|
if err != nil {
|
2018-10-31 18:10:03 +00:00
|
|
|
h.Logger.Debug("failed to decode request", zap.Error(err))
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
auth, err := pctx.GetAuthorizer(ctx)
|
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-21 16:05:55 +00:00
|
|
|
if err := h.TelegrafService.CreateTelegrafConfig(ctx, tc, auth.GetUserID()); err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
2019-07-09 15:16:26 +00:00
|
|
|
h.Logger.Debug("telegraf created", zap.String("telegraf", fmt.Sprint(tc)))
|
2018-10-24 15:13:30 +00:00
|
|
|
|
2019-01-03 19:48:26 +00:00
|
|
|
if err := encodeResponse(ctx, w, http.StatusCreated, newTelegrafResponse(tc, []*platform.Label{})); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-10-24 15:13:30 +00:00
|
|
|
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 {
|
2018-10-31 18:10:03 +00:00
|
|
|
h.Logger.Debug("failed to decode request", zap.Error(err))
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
auth, err := pctx.GetAuthorizer(ctx)
|
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-21 16:05:55 +00:00
|
|
|
tc, err = h.TelegrafService.UpdateTelegrafConfig(ctx, tc.ID, tc, auth.GetUserID())
|
2018-10-24 15:13:30 +00:00
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-18 19:03:36 +00:00
|
|
|
labels, err := h.LabelService.FindResourceLabels(ctx, platform.LabelMappingFilter{ResourceID: tc.ID})
|
2019-01-03 19:48:26 +00:00
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2019-01-03 19:48:26 +00:00
|
|
|
return
|
|
|
|
}
|
2019-07-09 15:16:26 +00:00
|
|
|
h.Logger.Debug("telegraf updated", zap.String("telegraf", fmt.Sprint(tc)))
|
2019-01-03 19:48:26 +00:00
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc, labels)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *TelegrafHandler) handleDeleteTelegraf(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
2019-12-04 01:00:15 +00:00
|
|
|
i, err := decodeGetTelegrafRequest(ctx)
|
2018-10-24 15:13:30 +00:00
|
|
|
if err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = h.TelegrafService.DeleteTelegrafConfig(ctx, i); err != nil {
|
2019-06-27 01:33:20 +00:00
|
|
|
h.HandleHTTPError(ctx, err, w)
|
2018-10-24 15:13:30 +00:00
|
|
|
return
|
|
|
|
}
|
2019-07-09 15:16:26 +00:00
|
|
|
h.Logger.Debug("telegraf deleted", zap.String("telegrafID", fmt.Sprint(i)))
|
2018-10-24 15:13:30 +00:00
|
|
|
|
2019-03-15 15:31:22 +00:00
|
|
|
w.WriteHeader(http.StatusNoContent)
|
2018-10-24 15:13:30 +00:00
|
|
|
}
|
2019-12-04 01:00:15 +00:00
|
|
|
|
|
|
|
// TelegrafService is an http client that speaks to the telegraf service via HTTP.
|
|
|
|
type TelegrafService struct {
|
|
|
|
client C
|
|
|
|
*UserResourceMappingService
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTelegrafService is a constructor for a telegraf service.
|
|
|
|
func NewTelegrafService(addr, token string, insecureSkipVerify bool) *TelegrafService {
|
|
|
|
return &TelegrafService{
|
|
|
|
client: C{
|
|
|
|
Addr: addr,
|
|
|
|
Token: token,
|
|
|
|
InsecureSkipVerify: insecureSkipVerify,
|
|
|
|
},
|
|
|
|
UserResourceMappingService: &UserResourceMappingService{
|
|
|
|
Addr: addr,
|
|
|
|
Token: token,
|
|
|
|
InsecureSkipVerify: insecureSkipVerify,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ platform.TelegrafConfigStore = (*TelegrafService)(nil)
|
|
|
|
|
|
|
|
// FindTelegrafConfigByID returns a single telegraf config by ID.
|
|
|
|
func (s *TelegrafService) FindTelegrafConfigByID(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
// FindTelegrafConfigs returns a list of telegraf configs that match filter and the total count of matching telegraf configs.
|
|
|
|
// Additional options provide pagination & sorting.
|
|
|
|
func (s *TelegrafService) FindTelegrafConfigs(ctx context.Context, filter platform.TelegrafConfigFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateTelegrafConfig creates a new telegraf config and sets b.ID with the new identifier.
|
|
|
|
func (s *TelegrafService) CreateTelegrafConfig(ctx context.Context, tc *platform.TelegrafConfig, userID platform.ID) error {
|
|
|
|
var body bytes.Buffer
|
|
|
|
if err := json.NewEncoder(&body).Encode(tc); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return s.client.post(telegrafsPath, &body).
|
|
|
|
RespFn(func(r *http.Response) error {
|
|
|
|
var teleResp platform.TelegrafConfig
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&teleResp); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// sad face >_<
|
|
|
|
*tc = teleResp
|
|
|
|
return nil
|
|
|
|
}).
|
|
|
|
Do(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateTelegrafConfig updates a single telegraf config.
|
|
|
|
// Returns the new telegraf config after update.
|
|
|
|
func (s *TelegrafService) UpdateTelegrafConfig(ctx context.Context, id platform.ID, tc *platform.TelegrafConfig, userID platform.ID) (*platform.TelegrafConfig, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteTelegrafConfig removes a telegraf config by ID.
|
|
|
|
func (s *TelegrafService) DeleteTelegrafConfig(ctx context.Context, id platform.ID) error {
|
|
|
|
return s.client.delete(path.Join(telegrafsPath, id.String())).Do(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// C is a basic http client that can make cReqs with out having to juggle
|
|
|
|
// the token and so forth. It provides sane defaults for checking response
|
|
|
|
// statuses, sets auth token when provided, and sets the content type to
|
|
|
|
// application/json for each request. The token, response checker, and
|
|
|
|
// content type can be overidden on the cReq as well.
|
|
|
|
type C struct {
|
|
|
|
Addr string
|
|
|
|
Token string
|
|
|
|
InsecureSkipVerify bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *C) delete(urlPath string) *cReq {
|
|
|
|
return c.newClientReq(http.MethodDelete, urlPath, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *C) post(urlPath string, body io.Reader) *cReq {
|
|
|
|
return c.newClientReq(http.MethodPost, urlPath, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *C) newClientReq(method, urlPath string, body io.Reader) *cReq {
|
|
|
|
u, err := NewURL(c.Addr, urlPath)
|
|
|
|
if err != nil {
|
|
|
|
return &cReq{err: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest(method, u.String(), body)
|
|
|
|
if err != nil {
|
|
|
|
return &cReq{err: err}
|
|
|
|
}
|
|
|
|
if c.Token != "" {
|
|
|
|
SetToken(c.Token, req)
|
|
|
|
}
|
|
|
|
|
|
|
|
cr := &cReq{
|
|
|
|
insecureSkip: c.InsecureSkipVerify,
|
|
|
|
req: req,
|
|
|
|
respFn: CheckError,
|
|
|
|
}
|
|
|
|
return cr.ContentType("application/json")
|
|
|
|
}
|
|
|
|
|
|
|
|
type cReq struct {
|
|
|
|
req *http.Request
|
|
|
|
insecureSkip bool
|
|
|
|
respFn func(*http.Response) error
|
|
|
|
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *cReq) Header(k, v string) *cReq {
|
|
|
|
if r.err != nil {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
r.req.Header.Add(k, v)
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *cReq) ContentType(ct string) *cReq {
|
|
|
|
return r.Header("Content-Type", ct)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *cReq) RespFn(fn func(*http.Response) error) *cReq {
|
|
|
|
r.respFn = fn
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *cReq) Do(ctx context.Context) error {
|
|
|
|
if r.err != nil {
|
|
|
|
return r.err
|
|
|
|
}
|
|
|
|
r.req = r.req.WithContext(ctx)
|
|
|
|
|
|
|
|
resp, err := NewClient(r.req.URL.Scheme, r.insecureSkip).Do(r.req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
io.Copy(ioutil.Discard, resp.Body) // drain body completely
|
|
|
|
resp.Body.Close()
|
|
|
|
}()
|
|
|
|
|
|
|
|
return r.respFn(resp)
|
|
|
|
}
|