mirror of
https://github.com/fleetdm/fleet
synced 2026-05-06 06:48:54 +00:00
# Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Documented any API changes (docs/Using-Fleet/REST-API.md or docs/Contributing/API-for-contributors.md) - [ ] Documented any permissions changes (docs/Using Fleet/manage-access.md) - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). Signed-off-by: guoguangwu <guoguangwu@magic-shield.com>
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/pkg/fleethttp"
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
|
)
|
|
|
|
const (
|
|
krContentTypeHeader = "Content-Type"
|
|
krTimestampHeader = "TimeStamp"
|
|
krPublishTopicURL = "%s/topics/%s"
|
|
krCheckTopicURL = "%s/topics/?topic=%s"
|
|
)
|
|
|
|
type KafkaRESTParams struct {
|
|
KafkaProxyHost string
|
|
KafkaTopic string
|
|
KafkaContentTypeValue string
|
|
KafkaTimeout int
|
|
}
|
|
|
|
type kafkaRESTProducer struct {
|
|
client *http.Client
|
|
URL string
|
|
CheckURL string
|
|
ContentTypeValue string
|
|
}
|
|
|
|
type kafkaRecords struct {
|
|
Records []kafkaValue `json:"records"`
|
|
}
|
|
|
|
type kafkaValue struct {
|
|
Value json.RawMessage `json:"value"`
|
|
}
|
|
|
|
func NewKafkaRESTWriter(p *KafkaRESTParams) (*kafkaRESTProducer, error) {
|
|
producer := &kafkaRESTProducer{
|
|
URL: fmt.Sprintf(krPublishTopicURL, p.KafkaProxyHost, p.KafkaTopic),
|
|
CheckURL: fmt.Sprintf(krCheckTopicURL, p.KafkaProxyHost, p.KafkaTopic),
|
|
client: fleethttp.NewClient(fleethttp.WithTimeout(time.Duration(p.KafkaTimeout) * time.Second)),
|
|
ContentTypeValue: p.KafkaContentTypeValue,
|
|
}
|
|
|
|
return producer, producer.checkTopic()
|
|
}
|
|
|
|
func (l *kafkaRESTProducer) Write(ctx context.Context, logs []json.RawMessage) error {
|
|
data := kafkaRecords{
|
|
Records: make([]kafkaValue, len(logs)),
|
|
}
|
|
|
|
for i, log := range logs {
|
|
data.Records[i] = kafkaValue{
|
|
Value: log,
|
|
}
|
|
}
|
|
|
|
output, err := json.Marshal(data)
|
|
if err != nil {
|
|
return ctxerr.Wrap(ctx, err, "kafka rest marshal")
|
|
}
|
|
|
|
resp, err := l.post(l.URL, bytes.NewBuffer(output))
|
|
if err != nil {
|
|
return ctxerr.Wrap(ctx, err, "kafka rest post")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
return checkResponse(resp)
|
|
}
|
|
|
|
func checkResponse(resp *http.Response) (err error) {
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("Error: %d. %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (l *kafkaRESTProducer) checkTopic() (err error) {
|
|
resp, err := l.client.Get(l.CheckURL)
|
|
if err != nil {
|
|
return fmt.Errorf("kafka rest topic check: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
return checkResponse(resp)
|
|
}
|
|
|
|
func (l *kafkaRESTProducer) post(url string, buf io.Reader) (*http.Response, error) {
|
|
req, err := http.NewRequest(http.MethodPost, url, buf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("kafka rest new request: %w", err)
|
|
}
|
|
|
|
now := float64(time.Now().UnixNano()) / float64(time.Second)
|
|
req.Header.Set(krContentTypeHeader, l.ContentTypeValue)
|
|
req.Header.Set(krTimestampHeader, fmt.Sprintf("%f", now))
|
|
|
|
return l.client.Do(req)
|
|
}
|