2016-10-25 15:20:06 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
|
2016-11-03 06:42:52 +00:00
|
|
|
"github.com/bouk/httprouter"
|
2016-10-25 15:20:06 +00:00
|
|
|
"github.com/influxdata/chronograf"
|
2016-11-03 06:42:52 +00:00
|
|
|
kapa "github.com/influxdata/chronograf/kapacitor"
|
2016-10-25 15:20:06 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type postKapacitorRequest struct {
|
|
|
|
Name *string `json:"name"` // User facing name of kapacitor instance.; Required: true
|
|
|
|
URL *string `json:"url"` // URL for the kapacitor backend (e.g. http://localhost:9092);/ Required: true
|
|
|
|
Username string `json:"username,omitempty"` // Username for authentication to kapacitor
|
|
|
|
Password string `json:"password,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *postKapacitorRequest) Valid() error {
|
|
|
|
if p.Name == nil || p.URL == nil {
|
|
|
|
return fmt.Errorf("name and url required")
|
|
|
|
}
|
|
|
|
|
|
|
|
url, err := url.ParseRequestURI(*p.URL)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid source URI: %v", err)
|
|
|
|
}
|
|
|
|
if len(url.Scheme) == 0 {
|
|
|
|
return fmt.Errorf("Invalid URL; no URL scheme defined")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type kapaLinks struct {
|
|
|
|
Proxy string `json:"proxy"` // URL location of proxy endpoint for this source
|
|
|
|
Self string `json:"self"` // Self link mapping to this resource
|
2016-11-03 06:59:09 +00:00
|
|
|
Tasks string `json:"tasks"` // Tasks link for defining task alerts for kapacitor
|
2016-10-25 15:20:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type kapacitor struct {
|
|
|
|
ID string `json:"id,string"` // Unique identifier representing a kapacitor instance.
|
|
|
|
Name string `json:"name"` // User facing name of kapacitor instance.
|
|
|
|
URL string `json:"url"` // URL for the kapacitor backend (e.g. http://localhost:9092)
|
|
|
|
Username string `json:"username,omitempty"` // Username for authentication to kapacitor
|
|
|
|
Password string `json:"password,omitempty"`
|
|
|
|
Links kapaLinks `json:"links"` // Links are URI locations related to kapacitor
|
|
|
|
}
|
|
|
|
|
2016-10-28 16:27:06 +00:00
|
|
|
// NewKapacitor adds valid kapacitor store store.
|
|
|
|
func (h *Service) NewKapacitor(w http.ResponseWriter, r *http.Request) {
|
2016-10-25 15:20:06 +00:00
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
_, err = h.SourcesStore.Get(ctx, srcID)
|
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var req postKapacitorRequest
|
|
|
|
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
|
|
invalidJSON(w)
|
|
|
|
return
|
|
|
|
}
|
2016-11-01 21:45:31 +00:00
|
|
|
if err := req.Valid(); err != nil {
|
2016-10-25 15:20:06 +00:00
|
|
|
invalidData(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srv := chronograf.Server{
|
|
|
|
SrcID: srcID,
|
|
|
|
Name: *req.Name,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
|
|
|
URL: *req.URL,
|
|
|
|
}
|
|
|
|
|
|
|
|
if srv, err = h.ServersStore.Add(ctx, srv); err != nil {
|
|
|
|
msg := fmt.Errorf("Error storing kapacitor %v: %v", req, err)
|
|
|
|
unknownErrorWithMessage(w, msg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
res := newKapacitor(srv)
|
|
|
|
w.Header().Add("Location", res.Links.Self)
|
|
|
|
encodeJSON(w, http.StatusCreated, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newKapacitor(srv chronograf.Server) kapacitor {
|
2016-10-28 16:27:06 +00:00
|
|
|
httpAPISrcs := "/chronograf/v1/sources"
|
2016-10-25 15:20:06 +00:00
|
|
|
return kapacitor{
|
|
|
|
ID: strconv.Itoa(srv.ID),
|
|
|
|
Name: srv.Name,
|
|
|
|
Username: srv.Username,
|
|
|
|
Password: srv.Password,
|
|
|
|
URL: srv.URL,
|
|
|
|
Links: kapaLinks{
|
|
|
|
Self: fmt.Sprintf("%s/%d/kapacitors/%d", httpAPISrcs, srv.SrcID, srv.ID),
|
|
|
|
Proxy: fmt.Sprintf("%s/%d/kapacitors/%d/proxy", httpAPISrcs, srv.SrcID, srv.ID),
|
2016-11-03 06:59:09 +00:00
|
|
|
Tasks: fmt.Sprintf("%s/%d/kapacitors/%d/tasks", httpAPISrcs, srv.SrcID, srv.ID),
|
2016-10-25 15:20:06 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type kapacitors struct {
|
|
|
|
Kapacitors []kapacitor `json:"kapacitors"`
|
|
|
|
}
|
|
|
|
|
2016-10-28 16:27:06 +00:00
|
|
|
// Kapacitors retrieves all kapacitors from store.
|
|
|
|
func (h *Service) Kapacitors(w http.ResponseWriter, r *http.Request) {
|
2016-10-25 15:20:06 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
mrSrvs, err := h.ServersStore.All(ctx)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusInternalServerError, "Error loading kapacitors")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srvs := make([]kapacitor, len(mrSrvs))
|
|
|
|
for i, srv := range mrSrvs {
|
|
|
|
srvs[i] = newKapacitor(srv)
|
|
|
|
}
|
|
|
|
|
|
|
|
res := kapacitors{
|
|
|
|
Kapacitors: srvs,
|
|
|
|
}
|
|
|
|
|
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
2016-10-28 16:27:06 +00:00
|
|
|
// KapacitorsID retrieves a kapacitor with ID from store.
|
|
|
|
func (h *Service) KapacitorsID(w http.ResponseWriter, r *http.Request) {
|
2016-10-25 15:20:06 +00:00
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
res := newKapacitor(srv)
|
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
2016-10-28 16:27:06 +00:00
|
|
|
// RemoveKapacitor deletes kapacitor from store.
|
|
|
|
func (h *Service) RemoveKapacitor(w http.ResponseWriter, r *http.Request) {
|
2016-10-25 15:20:06 +00:00
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = h.ServersStore.Delete(ctx, srv); err != nil {
|
|
|
|
unknownErrorWithMessage(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
|
|
|
type patchKapacitorRequest struct {
|
|
|
|
Name *string `json:"name,omitempty"` // User facing name of kapacitor instance.
|
|
|
|
URL *string `json:"url,omitempty"` // URL for the kapacitor
|
|
|
|
Username *string `json:"username,omitempty"` // Username for kapacitor auth
|
|
|
|
Password *string `json:"password,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *patchKapacitorRequest) Valid() error {
|
|
|
|
if p.URL != nil {
|
|
|
|
url, err := url.ParseRequestURI(*p.URL)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid source URI: %v", err)
|
|
|
|
}
|
|
|
|
if len(url.Scheme) == 0 {
|
|
|
|
return fmt.Errorf("Invalid URL; no URL scheme defined")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-28 16:27:06 +00:00
|
|
|
// UpdateKapacitor incrementally updates a kapacitor definition in the store
|
|
|
|
func (h *Service) UpdateKapacitor(w http.ResponseWriter, r *http.Request) {
|
2016-10-25 15:20:06 +00:00
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var req patchKapacitorRequest
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
|
|
invalidJSON(w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := req.Valid(); err != nil {
|
|
|
|
invalidData(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.Name != nil {
|
|
|
|
srv.Name = *req.Name
|
|
|
|
}
|
|
|
|
if req.URL != nil {
|
|
|
|
srv.URL = *req.URL
|
|
|
|
}
|
|
|
|
if req.Password != nil {
|
|
|
|
srv.Password = *req.Password
|
|
|
|
}
|
|
|
|
if req.Username != nil {
|
|
|
|
srv.Username = *req.Username
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := h.ServersStore.Update(ctx, srv); err != nil {
|
|
|
|
msg := fmt.Sprintf("Error updating kapacitor ID %d", id)
|
|
|
|
Error(w, http.StatusInternalServerError, msg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
res := newKapacitor(srv)
|
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
|
|
}
|
2016-10-31 23:11:05 +00:00
|
|
|
|
|
|
|
// KapacitorTasksPost proxies POST to kapacitor
|
|
|
|
func (h *Service) KapacitorTasksPost(w http.ResponseWriter, r *http.Request) {
|
2016-11-03 06:42:52 +00:00
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c := kapa.Client{
|
|
|
|
URL: srv.URL,
|
|
|
|
Username: srv.Username,
|
|
|
|
Password: srv.Password,
|
|
|
|
}
|
|
|
|
|
|
|
|
var rule chronograf.AlertRule
|
|
|
|
task, err := c.Create(ctx, rule)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusInternalServerError, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// TODO: Set the tickscript the store
|
|
|
|
// TODO: possibly use the Href in update to the store
|
|
|
|
_ = task.TICKScript
|
|
|
|
_ = task.ID
|
|
|
|
_ = task.Href
|
|
|
|
// TODO: Add the task from the store
|
|
|
|
// TODO: Return POST response
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
|
2016-10-31 23:11:05 +00:00
|
|
|
}
|
|
|
|
|
2016-11-03 06:42:52 +00:00
|
|
|
// KapacitorTasksPut proxies PATCH to kapacitor
|
|
|
|
func (h *Service) KapacitorTasksPut(w http.ResponseWriter, r *http.Request) {
|
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tid := httprouter.GetParamFromContext(ctx, "tid")
|
|
|
|
c := kapa.Client{
|
|
|
|
URL: srv.URL,
|
|
|
|
Username: srv.Username,
|
|
|
|
Password: srv.Password,
|
|
|
|
Ticker: &kapa.Alert{},
|
|
|
|
}
|
|
|
|
// TODO: Pull rule from PUT parameters
|
|
|
|
var rule chronograf.AlertRule
|
|
|
|
task, err := c.Update(ctx, c.Href(tid), rule)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusInternalServerError, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// TODO: Set the tickscript in the update to the store
|
|
|
|
// TODO: possibly use the Href in update to the store
|
|
|
|
_ = task.TICKScript
|
|
|
|
// TODO: Update the task from the store
|
|
|
|
// TODO: Return Patch response
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
2016-10-31 23:11:05 +00:00
|
|
|
}
|
|
|
|
|
2016-11-03 06:42:52 +00:00
|
|
|
// KapacitorTasksGet retrieves all tasks
|
2016-10-31 23:11:05 +00:00
|
|
|
func (h *Service) KapacitorTasksGet(w http.ResponseWriter, r *http.Request) {
|
2016-11-03 06:42:52 +00:00
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// TODO: GET tasks from store
|
|
|
|
}
|
|
|
|
|
|
|
|
// KapacitorTasksGet retrieves specific task
|
|
|
|
func (h *Service) KapacitorTasksID(w http.ResponseWriter, r *http.Request) {
|
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
tid := httprouter.GetParamFromContext(ctx, "tid")
|
|
|
|
// TODO: GET task from store
|
|
|
|
_ = tid
|
2016-10-31 23:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// KapacitorTasksDelete proxies DELETE to kapacitor
|
|
|
|
func (h *Service) KapacitorTasksDelete(w http.ResponseWriter, r *http.Request) {
|
2016-11-03 06:42:52 +00:00
|
|
|
id, err := paramID("kid", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
srv, err := h.ServersStore.Get(ctx, id)
|
|
|
|
if err != nil || srv.SrcID != srcID {
|
|
|
|
notFound(w, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Delete the task from the store
|
|
|
|
tid := httprouter.GetParamFromContext(ctx, "tid")
|
|
|
|
c := kapa.Client{
|
|
|
|
URL: srv.URL,
|
|
|
|
Username: srv.Username,
|
|
|
|
Password: srv.Password,
|
|
|
|
}
|
|
|
|
if err := c.Delete(ctx, c.Href(tid)); err != nil {
|
|
|
|
Error(w, http.StatusInternalServerError, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
2016-10-31 23:11:05 +00:00
|
|
|
}
|