Skip to content
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

[ASCII-145] Extract collector to a subpackage of status #20718

Merged
merged 6 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions cmd/agent/gui/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/DataDog/datadog-agent/comp/metadata/inventoryagent"
"github.com/DataDog/datadog-agent/pkg/config"
"github.com/DataDog/datadog-agent/pkg/status"
"github.com/DataDog/datadog-agent/pkg/status/collector"
"github.com/DataDog/datadog-agent/pkg/util/hostname"
"github.com/DataDog/datadog-agent/pkg/util/log"
"github.com/DataDog/datadog-agent/pkg/version"
Expand Down Expand Up @@ -53,14 +54,24 @@ func ping(w http.ResponseWriter, r *http.Request) {
func getStatus(w http.ResponseWriter, r *http.Request, invAgent inventoryagent.Component) {
statusType := mux.Vars(r)["type"]

verbose := r.URL.Query().Get("verbose") == "true"
status, e := status.GetStatus(verbose, invAgent)
if e != nil {
log.Errorf("Error getting status: " + e.Error())
w.Write([]byte("Error getting status: " + e.Error()))
var (
stats map[string]interface{}
err error
)
if statusType == "collector" {
stats = collector.GetStatusInfo()
} else {
verbose := r.URL.Query().Get("verbose") == "true"
stats, err = status.GetStatus(verbose, invAgent)
}

if err != nil {
log.Errorf("Error getting status: " + err.Error())
w.Write([]byte("Error getting status: " + err.Error()))
return
}
json, _ := json.Marshal(status)

json, _ := json.Marshal(stats)
html, e := renderStatus(json, statusType)
if e != nil {
w.Write([]byte("Error generating status html: " + e.Error()))
Expand All @@ -69,6 +80,7 @@ func getStatus(w http.ResponseWriter, r *http.Request, invAgent inventoryagent.C

w.Header().Set("Content-Type", "text/html")
w.Write([]byte(html))

}

// Sends the current agent version
Expand Down
90 changes: 90 additions & 0 deletions pkg/status/collector/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2023-present Datadog, Inc.

// Package collector fetch information needed to render the 'collector' section of the status page.
// This will, in time, be migrated to the collector package/comp.
package collector

import (
"encoding/json"
"expvar"
)

// GetStatusInfo retrives collector information
func GetStatusInfo() map[string]interface{} {
stats := make(map[string]interface{})

PopulateStatus(stats)

return stats
}

// PopulateStatus populates stats with collector information
func PopulateStatus(stats map[string]interface{}) {
runnerStatsJSON := []byte(expvar.Get("runner").String())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For each of these vars, runner, autoconfig, CheckScheduler, pyLoader, and pythonInit, how about extracting out a helper function that handles the json unmarshaling. So instead these variables can be handled like this:

stats["runnerStats"] = unmarshalStatsVar("runner") // returns nil if var doesn't exist, or can't unmarshal

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we could extract the functionality of collecting the expvar. I would suggest delaying it until we have extracted all status sub packages, as we might find better helper functions or even sub packages to deal with expvars parsing.

runnerStats := make(map[string]interface{})
json.Unmarshal(runnerStatsJSON, &runnerStats) //nolint:errcheck
stats["runnerStats"] = runnerStats

autoConfigStatsJSON := []byte(expvar.Get("autoconfig").String())
autoConfigStats := make(map[string]interface{})
json.Unmarshal(autoConfigStatsJSON, &autoConfigStats) //nolint:errcheck
stats["autoConfigStats"] = autoConfigStats

checkSchedulerStatsJSON := []byte(expvar.Get("CheckScheduler").String())
checkSchedulerStats := make(map[string]interface{})
json.Unmarshal(checkSchedulerStatsJSON, &checkSchedulerStats) //nolint:errcheck
stats["checkSchedulerStats"] = checkSchedulerStats

pyLoaderData := expvar.Get("pyLoader")
if pyLoaderData != nil {
pyLoaderStatsJSON := []byte(pyLoaderData.String())
pyLoaderStats := make(map[string]interface{})
json.Unmarshal(pyLoaderStatsJSON, &pyLoaderStats) //nolint:errcheck
stats["pyLoaderStats"] = pyLoaderStats
} else {
stats["pyLoaderStats"] = nil
}

pythonInitData := expvar.Get("pythonInit")
if pythonInitData != nil {
pythonInitJSON := []byte(pythonInitData.String())
pythonInit := make(map[string]interface{})
json.Unmarshal(pythonInitJSON, &pythonInit) //nolint:errcheck
stats["pythonInit"] = pythonInit
} else {
stats["pythonInit"] = nil
}

inventories := expvar.Get("inventories")
var inventoriesStats map[string]interface{}
if inventories != nil {
inventoriesStatsJSON := []byte(inventories.String())
json.Unmarshal(inventoriesStatsJSON, &inventoriesStats) //nolint:errcheck
}

checkMetadata := map[string]map[string]string{}
if data, ok := inventoriesStats["check_metadata"]; ok {
for _, instances := range data.(map[string]interface{}) {
for _, instance := range instances.([]interface{}) {
metadata := map[string]string{}
checkHash := ""
for k, v := range instance.(map[string]interface{}) {
if vStr, ok := v.(string); ok {
if k == "config.hash" {
checkHash = vStr
} else if k != "config.provider" {
metadata[k] = vStr
}
}
}
if checkHash != "" && len(metadata) != 0 {
checkMetadata[checkHash] = metadata
}
}
}
}
stats["inventories"] = checkMetadata
}
39 changes: 9 additions & 30 deletions pkg/status/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ func FormatStatus(data []byte) (string, error) {
} else {
log.Warn("The Forwarder status format is invalid. Some parts of the `Forwarder` section may be missing.")
}
runnerStats := stats["runnerStats"]
pyLoaderStats := stats["pyLoaderStats"]
pythonInit := stats["pythonInit"]
autoConfigStats := stats["autoConfigStats"]
checkSchedulerStats := stats["checkSchedulerStats"]

aggregatorStats := stats["aggregatorStats"]
s, err := checkstats.TranslateEventPlatformEventTypes(aggregatorStats)
if err != nil {
Expand All @@ -52,7 +48,6 @@ func FormatStatus(data []byte) (string, error) {
logsStats := stats["logsStats"]
dcaStats := stats["clusterAgentStatus"]
endpointsInfos := stats["endpointsInfos"]
inventoriesStats := stats["inventories"]
systemProbeStats := stats["systemProbeStats"]
processAgentStatus := stats["processAgentStatus"]
snmpTrapsStats := stats["snmpTrapsStats"]
Expand All @@ -63,7 +58,7 @@ func FormatStatus(data []byte) (string, error) {
var b = new(bytes.Buffer)
headerFunc := func() error { return RenderStatusTemplate(b, "/header.tmpl", stats) }
checkStatsFunc := func() error {
return renderChecksStats(b, runnerStats, pyLoaderStats, pythonInit, autoConfigStats, checkSchedulerStats, inventoriesStats, "")
return RenderStatusTemplate(b, "/collector.tmpl", stats)
}
jmxFetchFunc := func() error { return RenderStatusTemplate(b, "/jmxfetch.tmpl", stats) }
forwarderFunc := func() error { return RenderStatusTemplate(b, "/forwarder.tmpl", forwarderStats) }
Expand Down Expand Up @@ -146,9 +141,10 @@ func FormatDCAStatus(data []byte) (string, error) {
}

forwarderStats := stats["forwarderStats"]
runnerStats := stats["runnerStats"]
autoConfigStats := stats["autoConfigStats"]
checkSchedulerStats := stats["checkSchedulerStats"]
// We nil these keys because we do not want to display that information in the collector template
stats["pyLoaderStats"] = nil
stats["pythonInit"] = nil
stats["inventories"] = nil
hush-hush marked this conversation as resolved.
Show resolved Hide resolved
endpointsInfos := stats["endpointsInfos"]
logsStats := stats["logsStats"]
orchestratorStats := stats["orchestrator"]
Expand All @@ -160,7 +156,7 @@ func FormatDCAStatus(data []byte) (string, error) {
if err := RenderStatusTemplate(b, "/header.tmpl", stats); err != nil {
errs = append(errs, err)
}
if err := renderChecksStats(b, runnerStats, nil, nil, autoConfigStats, checkSchedulerStats, nil, ""); err != nil {
if err := RenderStatusTemplate(b, "/collector.tmpl", stats); err != nil {
errs = append(errs, err)
}
if err := RenderStatusTemplate(b, "/forwarder.tmpl", forwarderStats); err != nil {
Expand Down Expand Up @@ -271,32 +267,15 @@ func FormatMetadataMapCLI(data []byte) (string, error) {
return b.String(), nil
}

func renderChecksStats(w io.Writer, runnerStats, pyLoaderStats, pythonInit, autoConfigStats, checkSchedulerStats, inventoriesStats interface{}, onlyCheck string) error {
checkStats := make(map[string]interface{})
checkStats["RunnerStats"] = runnerStats
checkStats["pyLoaderStats"] = pyLoaderStats
checkStats["pythonInit"] = pythonInit
checkStats["AutoConfigStats"] = autoConfigStats
checkStats["CheckSchedulerStats"] = checkSchedulerStats
checkStats["OnlyCheck"] = onlyCheck
checkStats["CheckMetadata"] = inventoriesStats
return RenderStatusTemplate(w, "/collector.tmpl", checkStats)
}

func renderCheckStats(data []byte, checkName string) (string, error) {
stats, renderError, err := unmarshalStatus(data)
if renderError != "" || err != nil {
return renderError, err
}
runnerStats := stats["runnerStats"]
pyLoaderStats := stats["pyLoaderStats"]
pythonInit := stats["pythonInit"]
autoConfigStats := stats["autoConfigStats"]
checkSchedulerStats := stats["checkSchedulerStats"]
inventoriesStats := stats["inventories"]

var b = new(bytes.Buffer)
var errs []error
if err := renderChecksStats(b, runnerStats, pyLoaderStats, pythonInit, autoConfigStats, checkSchedulerStats, inventoriesStats, checkName); err != nil {
if err := RenderStatusTemplate(b, "/collector.tmpl", stats); err != nil {
errs = append(errs, err)
}
if err := renderErrors(b, errs); err != nil {
Expand Down
80 changes: 7 additions & 73 deletions pkg/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/DataDog/datadog-agent/pkg/config/utils"
logsStatus "github.com/DataDog/datadog-agent/pkg/logs/status"
"github.com/DataDog/datadog-agent/pkg/snmp/traps"
"github.com/DataDog/datadog-agent/pkg/status/collector"
"github.com/DataDog/datadog-agent/pkg/util/containers"
"github.com/DataDog/datadog-agent/pkg/util/flavor"
httputils "github.com/DataDog/datadog-agent/pkg/util/http"
Expand Down Expand Up @@ -125,13 +126,14 @@ func GetAndFormatStatus(invAgent inventoryagent.Component) ([]byte, error) {

// GetCheckStatusJSON gets the status of a single check as JSON
func GetCheckStatusJSON(c check.Check, cs *checkstats.Stats) ([]byte, error) {
stats := make(map[string]interface{})
stats = getRunnerStats(stats)
checks := stats["runnerStats"].(map[string]interface{})["Checks"].(map[string]interface{})
s := collector.GetStatusInfo()

checks := s["runnerStats"].(map[string]interface{})["Checks"].(map[string]interface{})

checks[c.String()] = make(map[checkid.ID]interface{})
checks[c.String()].(map[checkid.ID]interface{})[c.ID()] = cs

statusJSON, err := json.Marshal(stats)
statusJSON, err := json.Marshal(s)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -350,32 +352,14 @@ func getCommonStatus(invAgent inventoryagent.Component) (map[string]interface{},
return stats, nil
}

func getRunnerStats(stats map[string]interface{}) map[string]interface{} {
runnerStatsJSON := []byte(expvar.Get("runner").String())
runnerStats := make(map[string]interface{})
json.Unmarshal(runnerStatsJSON, &runnerStats) //nolint:errcheck
stats["runnerStats"] = runnerStats
return stats
}

func expvarStats(stats map[string]interface{}, invAgent inventoryagent.Component) (map[string]interface{}, error) {
var err error
forwarderStatsJSON := []byte(expvar.Get("forwarder").String())
forwarderStats := make(map[string]interface{})
json.Unmarshal(forwarderStatsJSON, &forwarderStats) //nolint:errcheck
stats["forwarderStats"] = forwarderStats

stats = getRunnerStats(stats)

autoConfigStatsJSON := []byte(expvar.Get("autoconfig").String())
autoConfigStats := make(map[string]interface{})
json.Unmarshal(autoConfigStatsJSON, &autoConfigStats) //nolint:errcheck
stats["autoConfigStats"] = autoConfigStats

checkSchedulerStatsJSON := []byte(expvar.Get("CheckScheduler").String())
checkSchedulerStats := make(map[string]interface{})
json.Unmarshal(checkSchedulerStatsJSON, &checkSchedulerStats) //nolint:errcheck
stats["checkSchedulerStats"] = checkSchedulerStats
collector.PopulateStatus(stats)

aggregatorStatsJSON := []byte(expvar.Get("aggregator").String())
aggregatorStats := make(map[string]interface{})
Expand Down Expand Up @@ -407,26 +391,6 @@ func expvarStats(stats map[string]interface{}, invAgent inventoryagent.Component
stats["dogstatsdStats"] = dogstatsdStats
}

pyLoaderData := expvar.Get("pyLoader")
if pyLoaderData != nil {
pyLoaderStatsJSON := []byte(pyLoaderData.String())
pyLoaderStats := make(map[string]interface{})
json.Unmarshal(pyLoaderStatsJSON, &pyLoaderStats) //nolint:errcheck
stats["pyLoaderStats"] = pyLoaderStats
} else {
stats["pyLoaderStats"] = nil
}

pythonInitData := expvar.Get("pythonInit")
if pythonInitData != nil {
pythonInitJSON := []byte(pythonInitData.String())
pythonInit := make(map[string]interface{})
json.Unmarshal(pythonInitJSON, &pythonInit) //nolint:errcheck
stats["pythonInit"] = pythonInit
} else {
stats["pythonInit"] = nil
}

hostnameStatsJSON := []byte(expvar.Get("hostname").String())
hostnameStats := make(map[string]interface{})
json.Unmarshal(hostnameStatsJSON, &hostnameStats) //nolint:errcheck
Expand All @@ -437,36 +401,6 @@ func expvarStats(stats map[string]interface{}, invAgent inventoryagent.Component
stats["ntpOffset"], err = strconv.ParseFloat(expvar.Get("ntpOffset").String(), 64)
}

inventories := expvar.Get("inventories")
var inventoriesStats map[string]interface{}
if inventories != nil {
inventoriesStatsJSON := []byte(inventories.String())
json.Unmarshal(inventoriesStatsJSON, &inventoriesStats) //nolint:errcheck
}

checkMetadata := map[string]map[string]string{}
if data, ok := inventoriesStats["check_metadata"]; ok {
for _, instances := range data.(map[string]interface{}) {
for _, instance := range instances.([]interface{}) {
metadata := map[string]string{}
checkHash := ""
for k, v := range instance.(map[string]interface{}) {
if vStr, ok := v.(string); ok {
if k == "config.hash" {
checkHash = vStr
} else if k != "config.provider" {
metadata[k] = vStr
}
}
}
if checkHash != "" && len(metadata) != 0 {
checkMetadata[checkHash] = metadata
}
}
}
}
stats["inventories"] = checkMetadata

// invAgent can be nil when generating a status page for some agent where inventory is not enabled
// (clusteragent, security-agent, ...).
//
Expand Down
12 changes: 6 additions & 6 deletions pkg/status/templates/collector.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Collector

Running Checks
==============
{{- with .RunnerStats }}
{{- with .runnerStats }}
{{- if and (not .Runs) (not .Checks)}}
No checks have run yet
{{end -}}
Expand All @@ -43,10 +43,10 @@ Collector
Average Execution Time : {{humanizeDuration .AverageExecutionTime "ms"}}
Last Execution Date : {{formatUnixTime .UpdateTimestamp}}
Last Successful Execution Date : {{ if .LastSuccessDate }}{{formatUnixTime .LastSuccessDate}}{{ else }}Never{{ end }}
{{- if $.CheckMetadata }}
{{- if index $.CheckMetadata .CheckID }}
{{- if $.inventories }}
{{- if index $.inventories .CheckID }}
metadata:
{{- range $k, $v := index $.CheckMetadata .CheckID }}
{{- range $k, $v := index $.inventories .CheckID }}
{{ $k }}: {{ $v }}
{{- end }}
{{- end }}
Expand Down Expand Up @@ -93,7 +93,7 @@ Collector
{{- end }}
{{- end }}

{{- with .AutoConfigStats }}
{{- with .autoConfigStats }}
{{- if .ConfigErrors}}
Config Errors
==============
Expand All @@ -105,7 +105,7 @@ Collector
{{- end}}
{{- end }}

{{- with .CheckSchedulerStats }}
{{- with .checkSchedulerStats }}
{{- if .LoaderErrors}}
Loading Errors
==============
Expand Down