internal/pkg/api/api.go (187 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package api
import (
"net/http"
"go.elastic.co/apm/v2"
"github.com/elastic/fleet-server/v7/internal/pkg/rollback"
"github.com/rs/zerolog/hlog"
)
type APIOpt func(a *apiServer)
func WithCheckin(ct *CheckinT) APIOpt {
return func(a *apiServer) {
a.ct = ct
}
}
func WithEnroller(et *EnrollerT) APIOpt {
return func(a *apiServer) {
a.et = et
}
}
func WithArtifact(at *ArtifactT) APIOpt {
return func(a *apiServer) {
a.at = at
}
}
func WithAck(ack *AckT) APIOpt {
return func(a *apiServer) {
a.ack = ack
}
}
func WithStatus(st *StatusT) APIOpt {
return func(a *apiServer) {
a.st = st
}
}
func WithUpload(ut *UploadT) APIOpt {
return func(a *apiServer) {
a.ut = ut
}
}
func WithFileDelivery(ft *FileDeliveryT) APIOpt {
return func(a *apiServer) {
a.ft = ft
}
}
func WithPGP(pt *PGPRetrieverT) APIOpt {
return func(a *apiServer) {
a.pt = pt
}
}
func WithAudit(audit *AuditT) APIOpt {
return func(a *apiServer) {
a.audit = audit
}
}
func WithTracer(tracer *apm.Tracer) APIOpt {
return func(a *apiServer) {
a.tracer = tracer
}
}
// FIXME: Cleanup needed for: metrics endpoint (actually a separate listener?), endpoint auth
// FIXME: Should we use strict handler
type apiServer struct {
ct *CheckinT
et *EnrollerT
at *ArtifactT
ack *AckT
st *StatusT
ut *UploadT
ft *FileDeliveryT
pt *PGPRetrieverT
audit *AuditT
// tracer is used by the wrapping server to instrument the API server
tracer *apm.Tracer
}
// ensure api implements the ServerInterface
var _ ServerInterface = (*apiServer)(nil)
func (a *apiServer) AgentEnroll(w http.ResponseWriter, r *http.Request, params AgentEnrollParams) {
zlog := hlog.FromRequest(r).With().Str("mod", kEnrollMod).Logger()
w.Header().Set("Content-Type", "application/json")
// Error in the scope for deferred rolback function check
var err error
// Initialize rollback/cleanup for enrollment
// This deletes all the artifacts that were created during enrollment
rb := rollback.New(zlog)
defer func() {
if err != nil {
zlog.Info().Err(err).Msg("perform rollback on enrollment failure")
err = rb.Rollback(r.Context())
if err != nil {
zlog.Error().Err(err).Msg("rollback error on enrollment failure")
}
}
}()
err = a.et.handleEnroll(zlog, w, r, rb, params.UserAgent)
if err != nil {
cntEnroll.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) AgentAcks(w http.ResponseWriter, r *http.Request, id string, params AgentAcksParams) {
zlog := hlog.FromRequest(r).With().Str(LogAgentID, id).Logger()
w.Header().Set("Content-Type", "application/json")
if err := a.ack.handleAcks(zlog, w, r, id); err != nil {
cntAcks.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) AgentCheckin(w http.ResponseWriter, r *http.Request, id string, params AgentCheckinParams) {
zlog := hlog.FromRequest(r).With().Str(LogAgentID, id).Logger()
w.Header().Set("Content-Type", "application/json")
err := a.ct.handleCheckin(zlog, w, r, id, params.UserAgent)
if err != nil {
cntCheckin.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) Artifact(w http.ResponseWriter, r *http.Request, id string, sha2 string, params ArtifactParams) {
zlog := hlog.FromRequest(r).With().
Str(LogAgentID, id).
Str("sha2", sha2).
Str("remoteAddr", r.RemoteAddr).
Logger()
err := a.at.handleArtifacts(zlog, w, r, id, sha2)
if err != nil {
w.Header().Set("Content-Type", "application/json")
cntArtifacts.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) UploadBegin(w http.ResponseWriter, r *http.Request, params UploadBeginParams) {
zlog := hlog.FromRequest(r).With().Logger()
w.Header().Set("Content-Type", "application/json")
err := a.ut.handleUploadBegin(zlog, w, r)
if err != nil {
cntUploadStart.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) UploadComplete(w http.ResponseWriter, r *http.Request, id string, params UploadCompleteParams) {
zlog := hlog.FromRequest(r).With().Str(LogAgentID, id).Logger()
w.Header().Set("Content-Type", "application/json")
err := a.ut.handleUploadComplete(zlog, w, r, id)
if err != nil {
cntUploadEnd.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) UploadChunk(w http.ResponseWriter, r *http.Request, id string, chunkNum int, params UploadChunkParams) {
zlog := hlog.FromRequest(r).With().Str(LogAgentID, id).Logger()
w.Header().Set("Content-Type", "application/json")
if _, err := a.ut.authAPIKey(r, a.ut.bulker, a.ut.cache); err != nil {
cntUploadChunk.IncError(err)
ErrorResp(w, r, err)
return
}
if err := a.ut.handleUploadChunk(zlog, w, r, id, chunkNum, params.XChunkSHA2); err != nil {
cntUploadChunk.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) GetFile(w http.ResponseWriter, r *http.Request, id string, params GetFileParams) {
zlog := hlog.FromRequest(r).With().Logger()
if err := a.ft.handleSendFile(zlog, w, r, id); err != nil {
cntFileDeliv.IncError(err)
w.Header().Set("Content-Type", "application/json")
ErrorResp(w, r, err)
}
}
func (a *apiServer) GetPGPKey(w http.ResponseWriter, r *http.Request, major, minor, patch int, params GetPGPKeyParams) {
zlog := hlog.FromRequest(r).With().Logger()
if err := a.pt.handlePGPKey(zlog, w, r, major, minor, patch); err != nil {
cntGetPGP.IncError(err)
w.Header().Set("Content-Type", "application/json")
ErrorResp(w, r, err)
}
}
func (a *apiServer) AuditUnenroll(w http.ResponseWriter, r *http.Request, id string, params AuditUnenrollParams) {
zlog := hlog.FromRequest(r).With().Str(LogAgentID, id).Logger()
if err := a.audit.handleUnenroll(zlog, w, r, id); err != nil {
w.Header().Set("Content-Type", "application/json")
cntAuditUnenroll.IncError(err)
ErrorResp(w, r, err)
}
}
func (a *apiServer) Status(w http.ResponseWriter, r *http.Request, params StatusParams) {
zlog := hlog.FromRequest(r).With().
Str("mod", kStatusMod).
Logger()
w.Header().Set("Content-Type", "application/json")
err := a.st.handleStatus(zlog, r, w)
if err != nil {
cntStatus.IncError(err)
ErrorResp(w, r, err)
}
}