diff --git a/cmd/agent/gui/agent.go b/cmd/agent/gui/agent.go index 04d86bca89ce2..3f2cb5e975d62 100644 --- a/cmd/agent/gui/agent.go +++ b/cmd/agent/gui/agent.go @@ -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" @@ -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())) @@ -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 diff --git a/pkg/status/collector/collector.go b/pkg/status/collector/collector.go new file mode 100644 index 0000000000000..f97ac72acd963 --- /dev/null +++ b/pkg/status/collector/collector.go @@ -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()) + 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 +} diff --git a/pkg/status/render.go b/pkg/status/render.go index b58fd4345a3ae..57fe0c6d67f55 100644 --- a/pkg/status/render.go +++ b/pkg/status/render.go @@ -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 { @@ -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"] @@ -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) } @@ -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 endpointsInfos := stats["endpointsInfos"] logsStats := stats["logsStats"] orchestratorStats := stats["orchestrator"] @@ -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 { @@ -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 { diff --git a/pkg/status/status.go b/pkg/status/status.go index aaaeeda84dc47..ea110dae7e2e1 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -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" @@ -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 } @@ -350,14 +352,6 @@ 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()) @@ -365,17 +359,7 @@ func expvarStats(stats map[string]interface{}, invAgent inventoryagent.Component 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{}) @@ -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 @@ -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, ...). // diff --git a/pkg/status/templates/collector.tmpl b/pkg/status/templates/collector.tmpl index 056b2f49323d5..e2e81e1d2b0eb 100644 --- a/pkg/status/templates/collector.tmpl +++ b/pkg/status/templates/collector.tmpl @@ -18,7 +18,7 @@ Collector Running Checks ============== -{{- with .RunnerStats }} +{{- with .runnerStats }} {{- if and (not .Runs) (not .Checks)}} No checks have run yet {{end -}} @@ -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 }} @@ -93,7 +93,7 @@ Collector {{- end }} {{- end }} -{{- with .AutoConfigStats }} +{{- with .autoConfigStats }} {{- if .ConfigErrors}} Config Errors ============== @@ -105,7 +105,7 @@ Collector {{- end}} {{- end }} -{{- with .CheckSchedulerStats }} +{{- with .checkSchedulerStats }} {{- if .LoaderErrors}} Loading Errors ==============