fleet/server/service/endpoint_campaigns.go
Zachary Wasserman ef1d69c371
Fix bugs in error handling for creation of live queries (#2181)
- Return the correct result type in error cases
- Handle nil values for campaign when logging
2020-01-13 17:35:42 -08:00

134 lines
4.4 KiB
Go

package service
import (
"context"
"encoding/json"
"net/http"
"github.com/go-kit/kit/endpoint"
kitlog "github.com/go-kit/kit/log"
"github.com/igm/sockjs-go/sockjs"
"github.com/kolide/fleet/server/contexts/viewer"
"github.com/kolide/fleet/server/kolide"
"github.com/kolide/fleet/server/websocket"
)
////////////////////////////////////////////////////////////////////////////////
// Create Distributed Query Campaign
////////////////////////////////////////////////////////////////////////////////
type createDistributedQueryCampaignRequest struct {
Query string `json:"query"`
Selected distributedQueryCampaignTargets `json:"selected"`
}
type distributedQueryCampaignTargets struct {
Labels []uint `json:"labels"`
Hosts []uint `json:"hosts"`
}
type createDistributedQueryCampaignResponse struct {
Campaign *kolide.DistributedQueryCampaign `json:"campaign,omitempty"`
Err error `json:"error,omitempty"`
}
func (r createDistributedQueryCampaignResponse) error() error { return r.Err }
func makeCreateDistributedQueryCampaignEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createDistributedQueryCampaignRequest)
campaign, err := svc.NewDistributedQueryCampaign(ctx, req.Query, req.Selected.Hosts, req.Selected.Labels)
if err != nil {
return createDistributedQueryCampaignResponse{Err: err}, nil
}
return createDistributedQueryCampaignResponse{Campaign: campaign}, nil
}
}
////////////////////////////////////////////////////////////////////////////////
// Create Distributed Query Campaign By Names
////////////////////////////////////////////////////////////////////////////////
type createDistributedQueryCampaignByNamesRequest struct {
Query string `json:"query"`
Selected distributedQueryCampaignTargetsByNames `json:"selected"`
}
type distributedQueryCampaignTargetsByNames struct {
Labels []string `json:"labels"`
Hosts []string `json:"hosts"`
}
func makeCreateDistributedQueryCampaignByNamesEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createDistributedQueryCampaignByNamesRequest)
campaign, err := svc.NewDistributedQueryCampaignByNames(ctx, req.Query, req.Selected.Hosts, req.Selected.Labels)
if err != nil {
return createDistributedQueryCampaignResponse{Err: err}, nil
}
return createDistributedQueryCampaignResponse{Campaign: campaign}, nil
}
}
////////////////////////////////////////////////////////////////////////////////
// Stream Distributed Query Campaign Results and Metadata
////////////////////////////////////////////////////////////////////////////////
func makeStreamDistributedQueryCampaignResultsHandler(svc kolide.Service, jwtKey string, logger kitlog.Logger) http.Handler {
opt := sockjs.DefaultOptions
opt.Websocket = true
opt.RawWebsocket = true
return sockjs.NewHandler("/api/v1/kolide/results", opt, func(session sockjs.Session) {
defer session.Close(0, "none")
conn := &websocket.Conn{Session: session}
// Receive the auth bearer token
token, err := conn.ReadAuthToken()
if err != nil {
logger.Log("err", err, "msg", "failed to read auth token")
return
}
// Authenticate with the token
vc, err := authViewer(context.Background(), jwtKey, token, svc)
if err != nil || !vc.CanPerformActions() {
logger.Log("err", err, "msg", "unauthorized viewer")
conn.WriteJSONError("unauthorized")
return
}
ctx := viewer.NewContext(context.Background(), *vc)
msg, err := conn.ReadJSONMessage()
if err != nil {
logger.Log("err", err, "msg", "reading select_campaign JSON")
conn.WriteJSONError("error reading select_campaign")
return
}
if msg.Type != "select_campaign" {
logger.Log("err", "unexpected msg type, expected select_campaign", "msg-type", msg.Type)
conn.WriteJSONError("expected select_campaign")
return
}
var info struct {
CampaignID uint `json:"campaign_id"`
}
err = json.Unmarshal(*(msg.Data.(*json.RawMessage)), &info)
if err != nil {
logger.Log("err", err, "msg", "unmarshaling select_campaign data")
conn.WriteJSONError("error unmarshaling select_campaign data")
return
}
if info.CampaignID == 0 {
logger.Log("err", "campaign ID not set")
conn.WriteJSONError("0 is not a valid campaign ID")
return
}
svc.StreamCampaignResults(ctx, conn, info.CampaignID)
})
}