-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Elastic Agent V2 status #1747
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,6 +193,12 @@ func (ct *CheckinT) processRequest(zlog zerolog.Logger, w http.ResponseWriter, r | |
return err | ||
} | ||
|
||
// Compare agent_components content and update if different | ||
rawComponents, err := parseComponents(zlog, agent, &req) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Resolve AckToken from request, fallback on the agent record | ||
seqno, err := ct.resolveSeqNo(ctx, zlog, req, agent) | ||
if err != nil { | ||
|
@@ -237,7 +243,7 @@ func (ct *CheckinT) processRequest(zlog zerolog.Logger, w http.ResponseWriter, r | |
defer longPoll.Stop() | ||
|
||
// Initial update on checkin, and any user fields that might have changed | ||
err = ct.bc.CheckIn(agent.Id, req.Status, rawMeta, seqno, ver) | ||
err = ct.bc.CheckIn(agent.Id, req.Status, req.Message, rawMeta, rawComponents, seqno, ver) | ||
if err != nil { | ||
zlog.Error().Err(err).Str("agent_id", agent.Id).Msg("checkin failed") | ||
} | ||
|
@@ -277,7 +283,7 @@ func (ct *CheckinT) processRequest(zlog zerolog.Logger, w http.ResponseWriter, r | |
zlog.Trace().Msg("fire long poll") | ||
break LOOP | ||
case <-tick.C: | ||
err := ct.bc.CheckIn(agent.Id, req.Status, nil, nil, ver) | ||
err := ct.bc.CheckIn(agent.Id, req.Status, req.Message, nil, rawComponents, nil, ver) | ||
if err != nil { | ||
zlog.Error().Err(err).Str("agent_id", agent.Id).Msg("checkin failed") | ||
} | ||
|
@@ -555,6 +561,60 @@ func parseMeta(zlog zerolog.Logger, agent *model.Agent, req *CheckinRequest) ([] | |
return outMeta, nil | ||
} | ||
|
||
func parseComponents(zlog zerolog.Logger, agent *model.Agent, req *CheckinRequest) ([]byte, error) { | ||
|
||
// Quick comparison first; compare the JSON payloads. | ||
// If the data is not consistently normalized, this short-circuit will not work. | ||
if bytes.Equal(req.Components, agent.Components) { | ||
zlog.Trace().Msg("quick comparing agent components data is equal") | ||
return nil, nil | ||
} | ||
|
||
// Deserialize the request components data | ||
var reqComponents interface{} | ||
if len(req.Components) > 0 { | ||
if err := json.Unmarshal(req.Components, &reqComponents); err != nil { | ||
return nil, errors.Wrap(err, "parseComponents request") | ||
} | ||
// Validate that components is an array | ||
if _, ok := reqComponents.([]interface{}); !ok { | ||
return nil, errors.Wrap(errors.New("components property is not array"), "parseComponents request") | ||
} | ||
} | ||
|
||
// If empty, don't step on existing data | ||
if reqComponents == nil { | ||
return nil, nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @aleksmaus We should probably assert the data we are receiving from Elastic Agent before sending that to Elasticsearch? Maybe we should also put some kind of limit, maybe a size based limit on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach is basically copied from parseMeta that is used currently for "local_metadata" handling which is another blob that agent can send whatever to the fleet server. Do we need to add field by field check? What are guarantees which fields are required vs which fields are optional? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will add strongly typed parsing to the "components" property tomorrow, only things that are specified in the V2 at the moment will be stored for the components, and it will need to be extended for anything extra in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, once everything is V2 we should be able to be more strict on any incoming data, not sure during the transition period we can do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. Will leave it as is for now until we stabilize the schema, can tighten validation later. |
||
|
||
// Deserialize the agent's components copy | ||
var agentComponents interface{} | ||
if len(agent.Components) > 0 { | ||
if err := json.Unmarshal(agent.Components, &agentComponents); err != nil { | ||
return nil, errors.Wrap(err, "parseComponents local") | ||
} | ||
} | ||
|
||
var outMeta []byte | ||
|
||
// Compare the deserialized meta structures and return the bytes to update if different | ||
if !reflect.DeepEqual(reqComponents, agentComponents) { | ||
|
||
zlog.Trace(). | ||
RawJSON("oldComponents", agent.Components). | ||
RawJSON("newComponents", req.Components). | ||
Msg("local components data is not equal") | ||
|
||
zlog.Info(). | ||
RawJSON("req.Components", req.Components). | ||
Msg("applying new components data") | ||
|
||
outMeta = req.Components | ||
} | ||
|
||
return outMeta, nil | ||
} | ||
|
||
func calcPollDuration(zlog zerolog.Logger, cfg *config.Server, setupDuration time.Duration) (time.Duration, time.Duration) { | ||
|
||
pollDuration := cfg.Timeouts.CheckinLongPoll | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -454,6 +454,15 @@ | |
"description": "Lst checkin status", | ||
"type": "string" | ||
}, | ||
"last_checkin_message": { | ||
"description": "Last checkin message", | ||
"type": "string" | ||
}, | ||
"components": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @joshdover Do we want to store the status, message, and components as they change across time in a datastream so we can track the changes of an Elastic Agent across time? I think from an alerting standpoint it would be beneficial but from a scalability it would add more data and load. |
||
"description": "Elastic Agent components detailed status information", | ||
"type": "object", | ||
"format": "raw" | ||
}, | ||
"default_api_key_id": { | ||
"description": "ID of the API key the Elastic Agent uses to authenticate with elasticsearch", | ||
"type": "string" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always forgot the
bytes.Equal
does check thelen
before doing the bytes check, I think.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach is copied from parseMeta, works fine for nils. It seems like the compiler is optimized for this as well
https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/bytes/bytes.go;l=19