2019-07-25 10:30:25 +00:00
package http
import (
"context"
"encoding/json"
"fmt"
2019-12-18 04:54:23 +00:00
"io/ioutil"
2019-07-25 10:30:25 +00:00
"net/http"
2020-02-26 16:34:10 +00:00
"path"
2019-12-27 18:14:34 +00:00
"time"
2019-07-25 10:30:25 +00:00
2020-02-26 16:34:10 +00:00
"github.com/influxdata/influxdb/kit/tracing"
2019-11-25 14:22:19 +00:00
"github.com/influxdata/httprouter"
2019-07-25 10:30:25 +00:00
"github.com/influxdata/influxdb"
2019-08-16 04:00:55 +00:00
pctx "github.com/influxdata/influxdb/context"
2019-07-25 10:30:25 +00:00
"github.com/influxdata/influxdb/notification/check"
2020-02-26 16:34:10 +00:00
"github.com/influxdata/influxdb/pkg/httpc"
2019-07-25 10:30:25 +00:00
"go.uber.org/zap"
)
// CheckBackend is all services and associated parameters required to construct
// the CheckBackendHandler.
type CheckBackend struct {
influxdb . HTTPErrorHandler
2019-12-04 23:10:23 +00:00
log * zap . Logger
2019-07-25 10:30:25 +00:00
2019-09-18 20:19:51 +00:00
TaskService influxdb . TaskService
2019-07-25 10:30:25 +00:00
CheckService influxdb . CheckService
UserResourceMappingService influxdb . UserResourceMappingService
LabelService influxdb . LabelService
UserService influxdb . UserService
OrganizationService influxdb . OrganizationService
}
// NewCheckBackend returns a new instance of CheckBackend.
2019-12-04 23:10:23 +00:00
func NewCheckBackend ( log * zap . Logger , b * APIBackend ) * CheckBackend {
2019-07-25 10:30:25 +00:00
return & CheckBackend {
HTTPErrorHandler : b . HTTPErrorHandler ,
2019-12-04 23:10:23 +00:00
log : log ,
2019-07-25 10:30:25 +00:00
2019-09-18 20:19:51 +00:00
TaskService : b . TaskService ,
2019-07-25 10:30:25 +00:00
CheckService : b . CheckService ,
UserResourceMappingService : b . UserResourceMappingService ,
LabelService : b . LabelService ,
UserService : b . UserService ,
OrganizationService : b . OrganizationService ,
}
}
// CheckHandler is the handler for the check service
type CheckHandler struct {
* httprouter . Router
influxdb . HTTPErrorHandler
2019-12-04 23:10:23 +00:00
log * zap . Logger
2019-07-25 10:30:25 +00:00
2019-09-18 20:19:51 +00:00
TaskService influxdb . TaskService
2019-07-25 10:30:25 +00:00
CheckService influxdb . CheckService
UserResourceMappingService influxdb . UserResourceMappingService
LabelService influxdb . LabelService
UserService influxdb . UserService
OrganizationService influxdb . OrganizationService
}
const (
2019-12-09 23:54:16 +00:00
prefixChecks = "/api/v2/checks"
2019-07-25 10:30:25 +00:00
checksIDPath = "/api/v2/checks/:id"
2019-08-22 16:18:02 +00:00
checksIDQueryPath = "/api/v2/checks/:id/query"
2019-07-25 10:30:25 +00:00
checksIDMembersPath = "/api/v2/checks/:id/members"
checksIDMembersIDPath = "/api/v2/checks/:id/members/:userID"
checksIDOwnersPath = "/api/v2/checks/:id/owners"
checksIDOwnersIDPath = "/api/v2/checks/:id/owners/:userID"
checksIDLabelsPath = "/api/v2/checks/:id/labels"
checksIDLabelsIDPath = "/api/v2/checks/:id/labels/:lid"
)
// NewCheckHandler returns a new instance of CheckHandler.
2019-12-04 23:10:23 +00:00
func NewCheckHandler ( log * zap . Logger , b * CheckBackend ) * CheckHandler {
2019-07-25 10:30:25 +00:00
h := & CheckHandler {
Router : NewRouter ( b . HTTPErrorHandler ) ,
HTTPErrorHandler : b . HTTPErrorHandler ,
2019-12-04 23:10:23 +00:00
log : log ,
2019-07-25 10:30:25 +00:00
CheckService : b . CheckService ,
UserResourceMappingService : b . UserResourceMappingService ,
LabelService : b . LabelService ,
UserService : b . UserService ,
2019-09-18 20:19:51 +00:00
TaskService : b . TaskService ,
2019-07-25 10:30:25 +00:00
OrganizationService : b . OrganizationService ,
}
2019-12-09 23:54:16 +00:00
h . HandlerFunc ( "POST" , prefixChecks , h . handlePostCheck )
h . HandlerFunc ( "GET" , prefixChecks , h . handleGetChecks )
2019-07-25 10:30:25 +00:00
h . HandlerFunc ( "GET" , checksIDPath , h . handleGetCheck )
2019-08-22 16:18:02 +00:00
h . HandlerFunc ( "GET" , checksIDQueryPath , h . handleGetCheckQuery )
2019-07-25 10:30:25 +00:00
h . HandlerFunc ( "DELETE" , checksIDPath , h . handleDeleteCheck )
h . HandlerFunc ( "PUT" , checksIDPath , h . handlePutCheck )
h . HandlerFunc ( "PATCH" , checksIDPath , h . handlePatchCheck )
memberBackend := MemberBackend {
HTTPErrorHandler : b . HTTPErrorHandler ,
2019-12-04 23:10:23 +00:00
log : b . log . With ( zap . String ( "handler" , "member" ) ) ,
2019-07-25 10:30:25 +00:00
ResourceType : influxdb . ChecksResourceType ,
UserType : influxdb . Member ,
UserResourceMappingService : b . UserResourceMappingService ,
UserService : b . UserService ,
}
h . HandlerFunc ( "POST" , checksIDMembersPath , newPostMemberHandler ( memberBackend ) )
h . HandlerFunc ( "GET" , checksIDMembersPath , newGetMembersHandler ( memberBackend ) )
h . HandlerFunc ( "DELETE" , checksIDMembersIDPath , newDeleteMemberHandler ( memberBackend ) )
ownerBackend := MemberBackend {
HTTPErrorHandler : b . HTTPErrorHandler ,
2019-12-04 23:10:23 +00:00
log : b . log . With ( zap . String ( "handler" , "member" ) ) ,
2019-07-25 10:30:25 +00:00
ResourceType : influxdb . ChecksResourceType ,
UserType : influxdb . Owner ,
UserResourceMappingService : b . UserResourceMappingService ,
UserService : b . UserService ,
}
h . HandlerFunc ( "POST" , checksIDOwnersPath , newPostMemberHandler ( ownerBackend ) )
h . HandlerFunc ( "GET" , checksIDOwnersPath , newGetMembersHandler ( ownerBackend ) )
h . HandlerFunc ( "DELETE" , checksIDOwnersIDPath , newDeleteMemberHandler ( ownerBackend ) )
labelBackend := & LabelBackend {
HTTPErrorHandler : b . HTTPErrorHandler ,
2019-12-04 23:10:23 +00:00
log : b . log . With ( zap . String ( "handler" , "label" ) ) ,
2019-07-25 10:30:25 +00:00
LabelService : b . LabelService ,
ResourceType : influxdb . TelegrafsResourceType ,
}
2019-09-25 07:07:24 +00:00
h . HandlerFunc ( "GET" , checksIDLabelsPath , newGetLabelsHandler ( labelBackend ) )
2019-07-25 10:30:25 +00:00
h . HandlerFunc ( "POST" , checksIDLabelsPath , newPostLabelHandler ( labelBackend ) )
h . HandlerFunc ( "DELETE" , checksIDLabelsIDPath , newDeleteLabelHandler ( labelBackend ) )
return h
}
type checkLinks struct {
Self string ` json:"self" `
Labels string ` json:"labels" `
Members string ` json:"members" `
Owners string ` json:"owners" `
2019-11-08 16:23:38 +00:00
Query string ` json:"query" `
2019-07-25 10:30:25 +00:00
}
type checkResponse struct {
influxdb . Check
2019-12-27 18:14:34 +00:00
Status string ` json:"status" `
Labels [ ] influxdb . Label ` json:"labels" `
Links checkLinks ` json:"links" `
LatestCompleted time . Time ` json:"latestCompleted,omitempty" `
LatestScheduled time . Time ` json:"latestScheduled,omitempty" `
LastRunStatus string ` json:"LastRunStatus,omitempty" `
LastRunError string ` json:"LastRunError,omitempty" `
2019-07-25 10:30:25 +00:00
}
2019-09-30 23:15:14 +00:00
type postCheckRequest struct {
influxdb . CheckCreate
Labels [ ] string ` json:"labels" `
}
type decodeLabels struct {
Labels [ ] string ` json:"labels" `
}
2019-07-25 10:30:25 +00:00
func ( resp checkResponse ) MarshalJSON ( ) ( [ ] byte , error ) {
b1 , err := json . Marshal ( resp . Check )
if err != nil {
return nil , err
}
b2 , err := json . Marshal ( struct {
2019-12-27 18:14:34 +00:00
Labels [ ] influxdb . Label ` json:"labels" `
Links checkLinks ` json:"links" `
Status string ` json:"status" `
LatestCompleted time . Time ` json:"latestCompleted,omitempty" `
LatestScheduled time . Time ` json:"latestScheduled,omitempty" `
LastRunStatus string ` json:"lastRunStatus,omitempty" `
LastRunError string ` json:"lastRunError,omitempty" `
2019-07-25 10:30:25 +00:00
} {
2019-12-27 18:14:34 +00:00
Links : resp . Links ,
Labels : resp . Labels ,
Status : resp . Status ,
LatestCompleted : resp . LatestCompleted ,
LatestScheduled : resp . LatestScheduled ,
LastRunStatus : resp . LastRunStatus ,
LastRunError : resp . LastRunError ,
2019-07-25 10:30:25 +00:00
} )
if err != nil {
return nil , err
}
return [ ] byte ( string ( b1 [ : len ( b1 ) - 1 ] ) + ", " + string ( b2 [ 1 : ] ) ) , nil
}
type checksResponse struct {
Checks [ ] * checkResponse ` json:"checks" `
Links * influxdb . PagingLinks ` json:"links" `
}
2019-09-18 20:19:51 +00:00
func ( h * CheckHandler ) newCheckResponse ( ctx context . Context , chk influxdb . Check , labels [ ] * influxdb . Label ) ( * checkResponse , error ) {
2019-09-24 19:48:36 +00:00
// TODO(desa): this should be handled in the check and not exposed in http land, but is currently blocking the FE. https://github.com/influxdata/influxdb/issues/15259
task , err := h . TaskService . FindTaskByID ( ctx , chk . GetTaskID ( ) )
if err != nil {
return nil , err
}
2019-08-07 22:34:07 +00:00
// Ensure that we don't expose that this creates a task behind the scene
chk . ClearPrivateData ( )
2019-07-25 10:30:25 +00:00
res := & checkResponse {
Check : chk ,
Links : checkLinks {
Self : fmt . Sprintf ( "/api/v2/checks/%s" , chk . GetID ( ) ) ,
Labels : fmt . Sprintf ( "/api/v2/checks/%s/labels" , chk . GetID ( ) ) ,
Members : fmt . Sprintf ( "/api/v2/checks/%s/members" , chk . GetID ( ) ) ,
Owners : fmt . Sprintf ( "/api/v2/checks/%s/owners" , chk . GetID ( ) ) ,
2019-11-08 16:23:38 +00:00
Query : fmt . Sprintf ( "/api/v2/checks/%s/query" , chk . GetID ( ) ) ,
2019-07-25 10:30:25 +00:00
} ,
2019-12-27 18:14:34 +00:00
Labels : [ ] influxdb . Label { } ,
LatestCompleted : task . LatestCompleted ,
LatestScheduled : task . LatestScheduled ,
LastRunStatus : task . LastRunStatus ,
LastRunError : task . LastRunError ,
2019-07-25 10:30:25 +00:00
}
for _ , l := range labels {
res . Labels = append ( res . Labels , * l )
}
2019-09-18 20:19:51 +00:00
res . Status = task . Status
return res , nil
2019-07-25 10:30:25 +00:00
}
2019-09-18 20:19:51 +00:00
func ( h * CheckHandler ) newChecksResponse ( ctx context . Context , chks [ ] influxdb . Check , labelService influxdb . LabelService , f influxdb . PagingFilter , opts influxdb . FindOptions ) * checksResponse {
2019-07-25 10:30:25 +00:00
resp := & checksResponse {
2019-09-18 20:19:51 +00:00
Checks : [ ] * checkResponse { } ,
2019-12-09 23:54:16 +00:00
Links : newPagingLinks ( prefixChecks , opts , f , len ( chks ) ) ,
2019-07-25 10:30:25 +00:00
}
2019-09-18 20:19:51 +00:00
for _ , chk := range chks {
2020-03-12 17:51:50 +00:00
labels , _ := labelService . FindResourceLabels ( ctx , influxdb . LabelMappingFilter { ResourceID : chk . GetID ( ) , ResourceType : influxdb . ChecksResourceType } )
2019-09-18 20:19:51 +00:00
cr , err := h . newCheckResponse ( ctx , chk , labels )
if err != nil {
2019-12-04 23:10:23 +00:00
h . log . Info ( "Failed to retrieve task associated with check" , zap . String ( "checkID" , chk . GetID ( ) . String ( ) ) )
2019-09-18 20:19:51 +00:00
continue
}
resp . Checks = append ( resp . Checks , cr )
2019-07-25 10:30:25 +00:00
}
return resp
}
func decodeGetCheckRequest ( ctx context . Context , r * http . Request ) ( i influxdb . ID , err error ) {
params := httprouter . ParamsFromContext ( ctx )
id := params . ByName ( "id" )
if id == "" {
return i , & influxdb . Error {
Code : influxdb . EInvalid ,
Msg : "url missing id" ,
}
}
if err := i . DecodeFromString ( id ) ; err != nil {
return i , err
}
return i , nil
}
func ( h * CheckHandler ) handleGetChecks ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
filter , opts , err := decodeCheckFilter ( ctx , r )
if err != nil {
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Failed to decode request" , zap . Error ( err ) )
2019-07-25 10:30:25 +00:00
h . HandleHTTPError ( ctx , err , w )
return
}
chks , _ , err := h . CheckService . FindChecks ( ctx , * filter , * opts )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Checks retrieved" , zap . String ( "checks" , fmt . Sprint ( chks ) ) )
2019-07-25 10:30:25 +00:00
2019-09-18 20:19:51 +00:00
if err := encodeResponse ( ctx , w , http . StatusOK , h . newChecksResponse ( ctx , chks , h . LabelService , filter , * opts ) ) ; err != nil {
2019-12-04 23:10:23 +00:00
logEncodingError ( h . log , r , err )
2019-07-25 10:30:25 +00:00
return
}
}
2019-08-22 16:18:02 +00:00
func ( h * CheckHandler ) handleGetCheckQuery ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
id , err := decodeGetCheckRequest ( ctx , r )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
chk , err := h . CheckService . FindCheckByID ( ctx , id )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
flux , err := chk . GenerateFlux ( )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Check query retrieved" , zap . String ( "check query" , flux ) )
2019-08-22 16:18:02 +00:00
if err := encodeResponse ( ctx , w , http . StatusOK , newFluxResponse ( flux ) ) ; err != nil {
2019-12-04 23:10:23 +00:00
logEncodingError ( h . log , r , err )
2019-08-22 16:18:02 +00:00
return
}
}
type fluxResp struct {
Flux string ` json:"flux" `
}
func newFluxResponse ( flux string ) fluxResp {
return fluxResp {
Flux : flux ,
}
}
2019-07-25 10:30:25 +00:00
func ( h * CheckHandler ) handleGetCheck ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
id , err := decodeGetCheckRequest ( ctx , r )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
chk , err := h . CheckService . FindCheckByID ( ctx , id )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Check retrieved" , zap . String ( "check" , fmt . Sprint ( chk ) ) )
2019-07-25 10:30:25 +00:00
2020-03-12 17:51:50 +00:00
labels , err := h . LabelService . FindResourceLabels ( ctx , influxdb . LabelMappingFilter { ResourceID : chk . GetID ( ) , ResourceType : influxdb . ChecksResourceType } )
2019-07-25 10:30:25 +00:00
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-09-18 20:19:51 +00:00
cr , err := h . newCheckResponse ( ctx , chk , labels )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
if err := encodeResponse ( ctx , w , http . StatusOK , cr ) ; err != nil {
2019-12-04 23:10:23 +00:00
logEncodingError ( h . log , r , err )
2019-07-25 10:30:25 +00:00
return
}
}
func decodeCheckFilter ( ctx context . Context , r * http . Request ) ( * influxdb . CheckFilter , * influxdb . FindOptions , error ) {
2019-10-21 18:11:08 +00:00
auth , err := pctx . GetAuthorizer ( ctx )
if err != nil {
return nil , nil , err
}
f := & influxdb . CheckFilter {
UserResourceMappingFilter : influxdb . UserResourceMappingFilter {
UserID : auth . GetUserID ( ) ,
ResourceType : influxdb . ChecksResourceType ,
} ,
}
2019-07-25 10:30:25 +00:00
2020-02-04 01:22:03 +00:00
opts , err := decodeFindOptions ( r )
2019-07-25 10:30:25 +00:00
if err != nil {
return f , nil , err
}
q := r . URL . Query ( )
if orgIDStr := q . Get ( "orgID" ) ; orgIDStr != "" {
orgID , err := influxdb . IDFromString ( orgIDStr )
if err != nil {
return f , opts , & influxdb . Error {
Code : influxdb . EInvalid ,
Msg : "orgID is invalid" ,
Err : err ,
}
}
f . OrgID = orgID
} else if orgNameStr := q . Get ( "org" ) ; orgNameStr != "" {
2020-02-26 16:08:49 +00:00
f . Org = & orgNameStr
2019-07-25 10:30:25 +00:00
}
return f , opts , err
}
2019-09-18 20:19:51 +00:00
type decodeStatus struct {
Status influxdb . Status ` json:"status" `
}
2019-12-18 04:54:23 +00:00
func decodePostCheckRequest ( r * http . Request ) ( postCheckRequest , error ) {
b , err := ioutil . ReadAll ( r . Body )
2019-07-25 10:30:25 +00:00
if err != nil {
2019-12-18 04:54:23 +00:00
return postCheckRequest { } , & influxdb . Error {
2019-07-25 10:30:25 +00:00
Code : influxdb . EInvalid ,
Err : err ,
}
}
defer r . Body . Close ( )
2019-12-18 01:55:52 +00:00
2019-12-18 04:54:23 +00:00
chk , err := check . UnmarshalJSON ( b )
2019-07-25 10:30:25 +00:00
if err != nil {
2019-12-18 04:54:23 +00:00
return postCheckRequest { } , & influxdb . Error {
2019-07-25 10:30:25 +00:00
Code : influxdb . EInvalid ,
Err : err ,
}
}
2019-08-16 11:12:28 +00:00
2019-09-18 20:19:51 +00:00
var ds decodeStatus
2019-12-18 01:55:52 +00:00
if err := json . Unmarshal ( b , & ds ) ; err != nil {
2019-12-18 04:54:23 +00:00
return postCheckRequest { } , & influxdb . Error {
2019-09-18 20:19:51 +00:00
Code : influxdb . EInvalid ,
Err : err ,
}
}
2019-09-30 23:15:14 +00:00
var dl decodeLabels
2019-12-18 01:55:52 +00:00
if err := json . Unmarshal ( b , & dl ) ; err != nil {
2019-12-18 04:54:23 +00:00
return postCheckRequest { } , & influxdb . Error {
2019-09-30 23:15:14 +00:00
Code : influxdb . EInvalid ,
Err : err ,
}
}
2019-09-18 20:19:51 +00:00
2019-12-18 04:54:23 +00:00
return postCheckRequest {
CheckCreate : influxdb . CheckCreate {
Check : chk ,
Status : ds . Status ,
} ,
Labels : dl . Labels ,
} , nil
2019-07-25 10:30:25 +00:00
}
2019-09-18 20:19:51 +00:00
func decodePutCheckRequest ( ctx context . Context , r * http . Request ) ( influxdb . CheckCreate , error ) {
2019-09-10 14:23:54 +00:00
params := httprouter . ParamsFromContext ( ctx )
id := params . ByName ( "id" )
if id == "" {
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate { } , & influxdb . Error {
2019-07-25 10:30:25 +00:00
Code : influxdb . EInvalid ,
2019-09-10 14:23:54 +00:00
Msg : "url missing id" ,
2019-07-25 10:30:25 +00:00
}
}
2019-09-10 14:23:54 +00:00
i := new ( influxdb . ID )
if err := i . DecodeFromString ( id ) ; err != nil {
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate { } , & influxdb . Error {
2019-09-10 14:23:54 +00:00
Code : influxdb . EInvalid ,
Msg : "invalid check id format" ,
}
}
2019-12-18 04:54:23 +00:00
b , err := ioutil . ReadAll ( r . Body )
2019-07-25 10:30:25 +00:00
if err != nil {
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate { } , & influxdb . Error {
2019-07-25 10:30:25 +00:00
Code : influxdb . EInvalid ,
2019-09-10 14:23:54 +00:00
Msg : "unable to read HTTP body" ,
2019-07-25 10:30:25 +00:00
Err : err ,
}
}
2019-12-18 04:54:23 +00:00
defer r . Body . Close ( )
2019-09-10 14:23:54 +00:00
2019-12-18 04:54:23 +00:00
chk , err := check . UnmarshalJSON ( b )
2019-09-10 14:23:54 +00:00
if err != nil {
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate { } , & influxdb . Error {
2019-07-25 10:30:25 +00:00
Code : influxdb . EInvalid ,
2019-09-10 14:23:54 +00:00
Msg : "malformed check body" ,
Err : err ,
2019-07-25 10:30:25 +00:00
}
}
2019-09-10 14:23:54 +00:00
chk . SetID ( * i )
if err := chk . Valid ( ) ; err != nil {
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate { } , err
2019-07-25 10:30:25 +00:00
}
2019-09-10 14:23:54 +00:00
2019-09-18 20:19:51 +00:00
var ds decodeStatus
2019-12-18 04:54:23 +00:00
err = json . Unmarshal ( b , & ds )
2019-09-18 20:19:51 +00:00
if err != nil {
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate { } , & influxdb . Error {
2019-09-18 20:19:51 +00:00
Code : influxdb . EInvalid ,
Err : err ,
}
}
2019-12-18 04:54:23 +00:00
return influxdb . CheckCreate {
Check : chk ,
Status : ds . Status ,
} , nil
2019-07-25 10:30:25 +00:00
}
type patchCheckRequest struct {
influxdb . ID
Update influxdb . CheckUpdate
}
func decodePatchCheckRequest ( ctx context . Context , r * http . Request ) ( * patchCheckRequest , error ) {
2019-12-18 04:54:23 +00:00
id := httprouter . ParamsFromContext ( ctx ) . ByName ( "id" )
2019-07-25 10:30:25 +00:00
if id == "" {
return nil , & influxdb . Error {
Code : influxdb . EInvalid ,
Msg : "url missing id" ,
}
}
var i influxdb . ID
if err := i . DecodeFromString ( id ) ; err != nil {
return nil , err
}
2019-12-18 04:54:23 +00:00
var upd influxdb . CheckUpdate
if err := json . NewDecoder ( r . Body ) . Decode ( & upd ) ; err != nil {
2019-07-25 10:30:25 +00:00
return nil , & influxdb . Error {
Code : influxdb . EInvalid ,
Msg : err . Error ( ) ,
}
}
if err := upd . Valid ( ) ; err != nil {
return nil , & influxdb . Error {
Code : influxdb . EInvalid ,
Msg : err . Error ( ) ,
}
}
2019-12-18 04:54:23 +00:00
return & patchCheckRequest {
ID : i ,
Update : upd ,
} , nil
2019-07-25 10:30:25 +00:00
}
// handlePostCheck is the HTTP handler for the POST /api/v2/checks route.
func ( h * CheckHandler ) handlePostCheck ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
2019-12-31 20:21:48 +00:00
2019-12-18 04:54:23 +00:00
chk , err := decodePostCheckRequest ( r )
2019-07-25 10:30:25 +00:00
if err != nil {
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Failed to decode request" , zap . Error ( err ) )
2019-07-25 10:30:25 +00:00
h . HandleHTTPError ( ctx , err , w )
return
}
2019-08-16 04:00:55 +00:00
auth , err := pctx . GetAuthorizer ( ctx )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-09-30 23:15:14 +00:00
if err := h . CheckService . CreateCheck ( ctx , chk . CheckCreate , auth . GetUserID ( ) ) ; err != nil {
2019-07-25 10:30:25 +00:00
h . HandleHTTPError ( ctx , err , w )
return
}
2019-09-30 23:15:14 +00:00
labels := h . mapNewCheckLabels ( ctx , chk . CheckCreate , chk . Labels )
cr , err := h . newCheckResponse ( ctx , chk , labels )
2019-09-18 20:19:51 +00:00
if err != nil {
h . HandleHTTPError ( ctx , err , w )
2019-09-24 19:48:36 +00:00
return
2019-09-18 20:19:51 +00:00
}
if err := encodeResponse ( ctx , w , http . StatusCreated , cr ) ; err != nil {
2019-12-04 23:10:23 +00:00
logEncodingError ( h . log , r , err )
2019-07-25 10:30:25 +00:00
return
}
}
2019-09-30 23:15:14 +00:00
// mapNewCheckLabels takes label ids from create check and maps them to the newly created check
func ( h * CheckHandler ) mapNewCheckLabels ( ctx context . Context , chk influxdb . CheckCreate , labels [ ] string ) [ ] * influxdb . Label {
var ls [ ] * influxdb . Label
for _ , sid := range labels {
var lid influxdb . ID
err := lid . DecodeFromString ( sid )
if err != nil {
continue
}
label , err := h . LabelService . FindLabelByID ( ctx , lid )
if err != nil {
continue
}
mapping := influxdb . LabelMapping {
LabelID : label . ID ,
ResourceID : chk . GetID ( ) ,
ResourceType : influxdb . ChecksResourceType ,
}
err = h . LabelService . CreateLabelMapping ( ctx , & mapping )
if err != nil {
continue
}
ls = append ( ls , label )
}
return ls
}
2019-07-25 10:30:25 +00:00
// handlePutCheck is the HTTP handler for the PUT /api/v2/checks route.
func ( h * CheckHandler ) handlePutCheck ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
chk , err := decodePutCheckRequest ( ctx , r )
if err != nil {
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Failed to decode request" , zap . Error ( err ) )
2019-07-25 10:30:25 +00:00
h . HandleHTTPError ( ctx , err , w )
return
}
2019-09-18 20:19:51 +00:00
c , err := h . CheckService . UpdateCheck ( ctx , chk . GetID ( ) , chk )
2019-07-25 10:30:25 +00:00
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2020-03-12 17:51:50 +00:00
labels , err := h . LabelService . FindResourceLabels ( ctx , influxdb . LabelMappingFilter { ResourceID : c . GetID ( ) , ResourceType : influxdb . ChecksResourceType } )
2019-07-25 10:30:25 +00:00
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Check replaced" , zap . String ( "check" , fmt . Sprint ( c ) ) )
2019-07-25 10:30:25 +00:00
2019-09-18 20:19:51 +00:00
cr , err := h . newCheckResponse ( ctx , c , labels )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
if err := encodeResponse ( ctx , w , http . StatusOK , cr ) ; err != nil {
2019-12-04 23:10:23 +00:00
logEncodingError ( h . log , r , err )
2019-07-25 10:30:25 +00:00
return
}
}
// handlePatchCheck is the HTTP handler for the PATCH /api/v2/checks/:id route.
func ( h * CheckHandler ) handlePatchCheck ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
req , err := decodePatchCheckRequest ( ctx , r )
if err != nil {
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Failed to decode request" , zap . Error ( err ) )
2019-07-25 10:30:25 +00:00
h . HandleHTTPError ( ctx , err , w )
return
}
chk , err := h . CheckService . PatchCheck ( ctx , req . ID , req . Update )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2020-03-12 17:51:50 +00:00
labels , err := h . LabelService . FindResourceLabels ( ctx , influxdb . LabelMappingFilter { ResourceID : chk . GetID ( ) , ResourceType : influxdb . ChecksResourceType } )
2019-07-25 10:30:25 +00:00
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Check patch" , zap . String ( "check" , fmt . Sprint ( chk ) ) )
2019-07-25 10:30:25 +00:00
2019-09-18 20:19:51 +00:00
cr , err := h . newCheckResponse ( ctx , chk , labels )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
if err := encodeResponse ( ctx , w , http . StatusOK , cr ) ; err != nil {
2019-12-04 23:10:23 +00:00
logEncodingError ( h . log , r , err )
2019-07-25 10:30:25 +00:00
return
}
}
func ( h * CheckHandler ) handleDeleteCheck ( w http . ResponseWriter , r * http . Request ) {
ctx := r . Context ( )
i , err := decodeGetCheckRequest ( ctx , r )
if err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
if err = h . CheckService . DeleteCheck ( ctx , i ) ; err != nil {
h . HandleHTTPError ( ctx , err , w )
return
}
2019-12-04 23:10:23 +00:00
h . log . Debug ( "Check deleted" , zap . String ( "checkID" , fmt . Sprint ( i ) ) )
2019-07-25 10:30:25 +00:00
w . WriteHeader ( http . StatusNoContent )
}
2020-02-26 16:34:10 +00:00
func checkIDPath ( id influxdb . ID ) string {
return path . Join ( prefixChecks , id . String ( ) )
}
// CheckService is a client to interact with the handlers in this package over HTTP.
// It does not implement influxdb.CheckService because it returns a concrete representation of the API response
// and influxdb.Check as returned by that interface is not appropriate for this use case.
type CheckService struct {
Client * httpc . Client
}
// FindCheckByID returns the Check matching the ID.
func ( s * CheckService ) FindCheckByID ( ctx context . Context , id influxdb . ID ) ( * Check , error ) {
span , _ := tracing . StartSpanFromContext ( ctx )
defer span . Finish ( )
var cr Check
err := s . Client .
Get ( checkIDPath ( id ) ) .
DecodeJSON ( & cr ) .
Do ( ctx )
if err != nil {
return nil , err
}
return & cr , nil
}
// FindCheck returns the first check matching the filter.
func ( s * CheckService ) FindCheck ( ctx context . Context , filter influxdb . CheckFilter ) ( * Check , error ) {
span , _ := tracing . StartSpanFromContext ( ctx )
defer span . Finish ( )
cs , n , err := s . FindChecks ( ctx , filter )
if err != nil {
return nil , err
}
if n == 0 && filter . Name != nil {
return nil , & influxdb . Error {
Code : influxdb . ENotFound ,
Op : influxdb . OpFindBucket ,
Msg : fmt . Sprintf ( "check %q not found" , * filter . Name ) ,
}
} else if n == 0 {
return nil , & influxdb . Error {
Code : influxdb . ENotFound ,
Op : influxdb . OpFindBucket ,
Msg : "check not found" ,
}
}
return cs [ 0 ] , nil
}
// FindChecks returns a list of checks that match filter and the total count of matching checks.
// Additional options provide pagination & sorting.
func ( s * CheckService ) FindChecks ( ctx context . Context , filter influxdb . CheckFilter , opt ... influxdb . FindOptions ) ( [ ] * Check , int , error ) {
span , _ := tracing . StartSpanFromContext ( ctx )
defer span . Finish ( )
params := findOptionParams ( opt ... )
if filter . OrgID != nil {
params = append ( params , [ 2 ] string { "orgID" , filter . OrgID . String ( ) } )
}
if filter . Org != nil {
params = append ( params , [ 2 ] string { "org" , * filter . Org } )
}
if filter . ID != nil {
params = append ( params , [ 2 ] string { "id" , filter . ID . String ( ) } )
}
if filter . Name != nil {
params = append ( params , [ 2 ] string { "name" , * filter . Name } )
}
var cr Checks
err := s . Client .
Get ( prefixChecks ) .
QueryParams ( params ... ) .
DecodeJSON ( & cr ) .
Do ( ctx )
if err != nil {
return nil , 0 , err
}
return cr . Checks , len ( cr . Checks ) , nil
}
// CreateCheck creates a new check.
func ( s * CheckService ) CreateCheck ( ctx context . Context , c * Check ) ( * Check , error ) {
span , _ := tracing . StartSpanFromContext ( ctx )
defer span . Finish ( )
var r Check
err := s . Client .
PostJSON ( c , prefixChecks ) .
DecodeJSON ( & r ) .
Do ( ctx )
if err != nil {
return nil , err
}
return & r , nil
}
// UpdateCheck updates a check.
func ( s * CheckService ) UpdateCheck ( ctx context . Context , id influxdb . ID , u * Check ) ( * Check , error ) {
span , _ := tracing . StartSpanFromContext ( ctx )
defer span . Finish ( )
var r Check
err := s . Client .
PutJSON ( u , checkIDPath ( id ) ) .
DecodeJSON ( & r ) .
Do ( ctx )
if err != nil {
return nil , err
}
return & r , nil
}
// PatchCheck changes the status, description or name of a check.
func ( s * CheckService ) PatchCheck ( ctx context . Context , id influxdb . ID , u influxdb . CheckUpdate ) ( * Check , error ) {
span , _ := tracing . StartSpanFromContext ( ctx )
defer span . Finish ( )
var r Check
err := s . Client .
PutJSON ( u , checkIDPath ( id ) ) .
DecodeJSON ( & r ) .
Do ( ctx )
if err != nil {
return nil , err
}
return & r , nil
}
// DeleteCheck removes a check.
func ( s * CheckService ) DeleteCheck ( ctx context . Context , id influxdb . ID ) error {
return s . Client .
Delete ( checkIDPath ( id ) ) .
Do ( ctx )
}
// TODO(gavincabbage): These structures should be in a common place, like other models,
// but the common influxdb.Check is an interface that is not appropriate for an API client.
type Checks struct {
Checks [ ] * Check ` json:"checks" `
Links * influxdb . PagingLinks ` json:"links" `
}
type Check struct {
ID influxdb . ID ` json:"id,omitempty" `
Name string ` json:"name" `
OrgID influxdb . ID ` json:"orgID,omitempty" `
OwnerID influxdb . ID ` json:"ownerID,omitempty" `
CreatedAt time . Time ` json:"createdAt,omitempty" `
UpdatedAt time . Time ` json:"updatedAt,omitempty" `
Query * CheckQuery ` json:"query" `
Status influxdb . Status ` json:"status" `
Description string ` json:"description" `
LatestCompleted time . Time ` json:"latestCompleted" `
LastRunStatus string ` json:"lastRunStatus" `
LastRunError string ` json:"lastRunError" `
Labels [ ] * influxdb . Label ` json:"labels" `
Links * CheckLinks ` json:"links" `
Type string ` json:"type" `
TimeSince string ` json:"timeSince" `
StaleTime string ` json:"staleTime" `
ReportZero bool ` json:"reportZero" `
Level string ` json:"level" `
Every string ` json:"every" `
Offset string ` json:"offset" `
Tags [ ] * influxdb . Tag ` json:"tags" `
StatusMessageTemplate string ` json:"statusMessageTemplate" `
Thresholds [ ] * CheckThreshold ` json:"thresholds" `
}
type CheckQuery struct {
Text string ` json:"text" `
EditMode string ` json:"editMode" `
Name string ` json:"name" `
BuilderConfig * CheckBuilderConfig ` json:"builderConfig" `
}
type CheckBuilderConfig struct {
Buckets [ ] string ` json:"buckets" `
Tags [ ] struct {
Key string ` json:"key" `
Values [ ] string ` json:"values" `
AggregateFunctionType string ` json:"aggregateFunctionType" `
} ` json:"tags" `
Functions [ ] struct {
Name string ` json:"name" `
} ` json:"functions" `
AggregateWindow struct {
Period string ` json:"period" `
} ` json:"aggregateWindow" `
}
type CheckLinks struct {
Self string ` json:"self" `
Labels string ` json:"labels" `
Members string ` json:"members" `
Owners string ` json:"owners" `
Query string ` json:"query" `
}
type CheckThreshold struct {
check . ThresholdConfigBase
Type string ` json:"type" `
Value float64 ` json:"value,omitempty" `
Min float64 ` json:"min,omitempty" `
Max float64 ` json:"max,omitempty" `
Within bool ` json:"within" `
}