summaryrefslogtreecommitdiffstats
path: root/src/go/plugin/go.d/modules/puppet
diff options
context:
space:
mode:
Diffstat (limited to '')
l---------src/go/plugin/go.d/modules/puppet/README.md (renamed from collectors/python.d.plugin/puppet/README.md)0
-rw-r--r--src/go/plugin/go.d/modules/puppet/charts.go93
-rw-r--r--src/go/plugin/go.d/modules/puppet/collect.go75
-rw-r--r--src/go/plugin/go.d/modules/puppet/config_schema.json177
-rw-r--r--src/go/plugin/go.d/modules/puppet/integrations/puppet.md233
-rw-r--r--src/go/plugin/go.d/modules/puppet/metadata.yaml184
-rw-r--r--src/go/plugin/go.d/modules/puppet/puppet.go114
-rw-r--r--src/go/plugin/go.d/modules/puppet/puppet_test.go252
-rw-r--r--src/go/plugin/go.d/modules/puppet/response.go32
-rw-r--r--src/go/plugin/go.d/modules/puppet/testdata/config.json20
-rw-r--r--src/go/plugin/go.d/modules/puppet/testdata/config.yaml17
-rw-r--r--src/go/plugin/go.d/modules/puppet/testdata/serviceStatusResponse.json497
12 files changed, 1694 insertions, 0 deletions
diff --git a/collectors/python.d.plugin/puppet/README.md b/src/go/plugin/go.d/modules/puppet/README.md
index b6c4c83f9..b6c4c83f9 120000
--- a/collectors/python.d.plugin/puppet/README.md
+++ b/src/go/plugin/go.d/modules/puppet/README.md
diff --git a/src/go/plugin/go.d/modules/puppet/charts.go b/src/go/plugin/go.d/modules/puppet/charts.go
new file mode 100644
index 000000000..c1da8d162
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/charts.go
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package puppet
+
+import (
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+)
+
+const (
+ prioJVMHeap = module.Priority + iota
+ prioJVMNonHeap
+ prioCPUUsage
+ prioFileDescriptors
+)
+
+const (
+ byteToMiB = 1 << 20
+)
+
+var charts = module.Charts{
+ jvmHeapChart.Copy(),
+ jvmNonHeapChart.Copy(),
+ cpuUsageChart.Copy(),
+ fileDescriptorsChart.Copy(),
+}
+
+var (
+ jvmHeapChart = module.Chart{
+ ID: "jvm_heap",
+ Title: "JVM Heap",
+ Units: "MiB",
+ Fam: "resources",
+ Ctx: "puppet.jvm_heap",
+ Type: module.Area,
+ Priority: prioJVMHeap,
+ Dims: module.Dims{
+ {ID: "jvm_heap_committed", Name: "committed", Div: byteToMiB},
+ {ID: "jvm_heap_used", Name: "used", Div: byteToMiB},
+ },
+ Vars: module.Vars{
+ {ID: "jvm_heap_max"},
+ {ID: "jvm_heap_init"},
+ },
+ }
+
+ jvmNonHeapChart = module.Chart{
+ ID: "jvm_nonheap",
+ Title: "JVM Non-Heap",
+ Units: "MiB",
+ Fam: "resources",
+ Ctx: "puppet.jvm_nonheap",
+ Type: module.Area,
+ Priority: prioJVMNonHeap,
+ Dims: module.Dims{
+ {ID: "jvm_nonheap_committed", Name: "committed", Div: byteToMiB},
+ {ID: "jvm_nonheap_used", Name: "used", Div: byteToMiB},
+ },
+ Vars: module.Vars{
+ {ID: "jvm_nonheap_max"},
+ {ID: "jvm_nonheap_init"},
+ },
+ }
+
+ cpuUsageChart = module.Chart{
+ ID: "cpu",
+ Title: "CPU usage",
+ Units: "percentage",
+ Fam: "resources",
+ Ctx: "puppet.cpu",
+ Type: module.Stacked,
+ Priority: prioCPUUsage,
+ Dims: module.Dims{
+ {ID: "cpu_usage", Name: "execution", Div: 1000},
+ {ID: "gc_cpu_usage", Name: "GC", Div: 1000},
+ },
+ }
+
+ fileDescriptorsChart = module.Chart{
+ ID: "fd_open",
+ Title: "File Descriptors",
+ Units: "descriptors",
+ Fam: "resources",
+ Ctx: "puppet.fdopen",
+ Type: module.Line,
+ Priority: prioFileDescriptors,
+ Dims: module.Dims{
+ {ID: "fd_used", Name: "used"},
+ },
+ Vars: module.Vars{
+ {ID: "fd_max"},
+ },
+ }
+)
diff --git a/src/go/plugin/go.d/modules/puppet/collect.go b/src/go/plugin/go.d/modules/puppet/collect.go
new file mode 100644
index 000000000..a1b95e09c
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/collect.go
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package puppet
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+var (
+ //https://puppet.com/docs/puppet/8/server/status-api/v1/services
+ urlPathStatusService = "/status/v1/services"
+ urlQueryStatusService = url.Values{"level": {"debug"}}.Encode()
+)
+
+func (p *Puppet) collect() (map[string]int64, error) {
+ stats, err := p.queryStatsService()
+ if err != nil {
+ return nil, err
+ }
+
+ mx := stm.ToMap(stats)
+
+ return mx, nil
+}
+
+func (p *Puppet) queryStatsService() (*statusServiceResponse, error) {
+ req, err := web.NewHTTPRequestWithPath(p.Request, urlPathStatusService)
+ if err != nil {
+ return nil, err
+ }
+
+ req.URL.RawQuery = urlQueryStatusService
+
+ var stats statusServiceResponse
+ if err := p.doOKDecode(req, &stats); err != nil {
+ return nil, err
+ }
+
+ if stats.StatusService == nil {
+ return nil, fmt.Errorf("unexpected response: not puppet service status data")
+ }
+
+ return &stats, nil
+}
+
+func (p *Puppet) doOKDecode(req *http.Request, in interface{}) error {
+ resp, err := p.httpClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
+ }
+ defer closeBody(resp)
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
+ }
+
+ if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
+ return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
+ }
+ return nil
+}
+
+func closeBody(resp *http.Response) {
+ if resp != nil && resp.Body != nil {
+ _, _ = io.Copy(io.Discard, resp.Body)
+ _ = resp.Body.Close()
+ }
+}
diff --git a/src/go/plugin/go.d/modules/puppet/config_schema.json b/src/go/plugin/go.d/modules/puppet/config_schema.json
new file mode 100644
index 000000000..92cbcb87f
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/config_schema.json
@@ -0,0 +1,177 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Puppet collector configuration.",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 1
+ },
+ "url": {
+ "title": "URL",
+ "description": "The base URL where the Puppet instance can be accessed.",
+ "type": "string",
+ "default": "https://127.0.0.1:8140",
+ "format": "uri"
+ },
+ "timeout": {
+ "title": "Timeout",
+ "description": "The timeout in seconds for the HTTP request.",
+ "type": "number",
+ "minimum": 0.5,
+ "default": 1
+ },
+ "not_follow_redirects": {
+ "title": "Not follow redirects",
+ "description": "If set, the client will not follow HTTP redirects automatically.",
+ "type": "boolean"
+ },
+ "username": {
+ "title": "Username",
+ "description": "The username for basic authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "password": {
+ "title": "Password",
+ "description": "The password for basic authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "proxy_url": {
+ "title": "Proxy URL",
+ "description": "The URL of the proxy server.",
+ "type": "string"
+ },
+ "proxy_username": {
+ "title": "Proxy username",
+ "description": "The username for proxy authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "proxy_password": {
+ "title": "Proxy password",
+ "description": "The password for proxy authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "headers": {
+ "title": "Headers",
+ "description": "Additional HTTP headers to include in the request.",
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "tls_skip_verify": {
+ "title": "Skip TLS verification",
+ "description": "If set, TLS certificate verification will be skipped.",
+ "type": "boolean"
+ },
+ "tls_ca": {
+ "title": "TLS CA",
+ "description": "The path to the CA certificate file for TLS verification.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "tls_cert": {
+ "title": "TLS certificate",
+ "description": "The path to the client certificate file for TLS authentication.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "tls_key": {
+ "title": "TLS key",
+ "description": "The path to the client key file for TLS authentication.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "body": {
+ "title": "Body",
+ "type": "string"
+ },
+ "method": {
+ "title": "Method",
+ "type": "string"
+ }
+ },
+ "required": [
+ "url"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ }
+ },
+ "uiSchema": {
+ "uiOptions": {
+ "fullPage": true
+ },
+ "body": {
+ "ui:widget": "hidden"
+ },
+ "method": {
+ "ui:widget": "hidden"
+ },
+ "timeout": {
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
+ },
+ "password": {
+ "ui:widget": "password"
+ },
+ "proxy_password": {
+ "ui:widget": "password"
+ },
+ "ui:flavour": "tabs",
+ "ui:options": {
+ "tabs": [
+ {
+ "title": "Base",
+ "fields": [
+ "update_every",
+ "url",
+ "timeout",
+ "not_follow_redirects"
+ ]
+ },
+ {
+ "title": "Auth",
+ "fields": [
+ "username",
+ "password"
+ ]
+ },
+ {
+ "title": "TLS",
+ "fields": [
+ "tls_skip_verify",
+ "tls_ca",
+ "tls_cert",
+ "tls_key"
+ ]
+ },
+ {
+ "title": "Proxy",
+ "fields": [
+ "proxy_url",
+ "proxy_username",
+ "proxy_password"
+ ]
+ },
+ {
+ "title": "Headers",
+ "fields": [
+ "headers"
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/src/go/plugin/go.d/modules/puppet/integrations/puppet.md b/src/go/plugin/go.d/modules/puppet/integrations/puppet.md
new file mode 100644
index 000000000..23e85dc4d
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/integrations/puppet.md
@@ -0,0 +1,233 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/puppet/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/puppet/metadata.yaml"
+sidebar_label: "Puppet"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/CICD Platforms"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# Puppet
+
+
+<img src="https://netdata.cloud/img/puppet.svg" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: puppet
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+This collector monitors Puppet metrics, including JVM heap and non-heap memory, CPU usage, and file descriptors.
+
+
+It uses Puppet's metrics API endpoint [/status/v1/services](https://www.puppet.com/docs/puppetserver/5.3/status-api/v1/services.html) to gather the metrics.
+
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+By default, it detects Puppet instances running on localhost that are listening on port 8140.
+On startup, it tries to collect metrics from:
+
+- https://127.0.0.1:8140
+
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per Puppet instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| puppet.jvm_heap | committed, used | MiB |
+| puppet.jvm_nonheap | committed, used | MiB |
+| puppet.cpu | execution, GC | percentage |
+| puppet.fdopen | used | descriptors |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+No action required.
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/puppet.conf`.
+
+
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config go.d/puppet.conf
+```
+#### Options
+
+The following options can be defined globally: update_every, autodetection_retry.
+
+
+<details open><summary></summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| url | The base URL where the Puppet instance can be accessed. | https://127.0.0.1:8140 | yes |
+| timeout | HTTPS request timeout. | 1 | no |
+| username | Username for basic HTTPS authentication. | | no |
+| password | Password for basic HTTPS authentication. | | no |
+| proxy_url | Proxy URL. | | no |
+| proxy_username | Username for proxy basic HTTPS authentication. | | no |
+| proxy_password | Password for proxy basic HTTPS authentication. | | no |
+| method | HTTPS request method. | POST | no |
+| body | HTTPS request body. | | no |
+| headers | HTTPS request headers. | | no |
+| not_follow_redirects | Redirect handling policy. Controls whether the client follows redirects. | no | no |
+| tls_skip_verify | Server certificate chain and hostname validation policy. Controls whether the client performs this check. | no | no |
+| tls_ca | Certification authority that the client uses when verifying the server's certificates. | | no |
+| tls_cert | Client TLS certificate. | | no |
+| tls_key | Client TLS key. | | no |
+
+</details>
+
+#### Examples
+
+##### Basic with self-signed certificate
+
+Puppet with self-signed TLS certificate.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: https://127.0.0.1:8140
+ tls_skip_verify: yes
+
+```
+</details>
+
+##### Multi-instance
+
+> **Note**: When you define multiple jobs, their names must be unique.
+
+Collecting metrics from local and remote instances.
+
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: https://127.0.0.1:8140
+ tls_skip_verify: yes
+
+ - name: remote
+ url: https://192.0.2.1:8140
+ tls_skip_verify: yes
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature.
+
+To troubleshoot issues with the `puppet` collector, run the `go.d.plugin` with the debug option enabled. The output
+should give you clues as to why the collector isn't working.
+
+- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
+ your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
+
+ ```bash
+ cd /usr/libexec/netdata/plugins.d/
+ ```
+
+- Switch to the `netdata` user.
+
+ ```bash
+ sudo -u netdata -s
+ ```
+
+- Run the `go.d.plugin` to debug the collector:
+
+ ```bash
+ ./go.d.plugin -d -m puppet
+ ```
+
+### Getting Logs
+
+If you're encountering problems with the `puppet` collector, follow these steps to retrieve logs and identify potential issues:
+
+- **Run the command** specific to your system (systemd, non-systemd, or Docker container).
+- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem.
+
+#### System with systemd
+
+Use the following command to view logs generated since the last Netdata service restart:
+
+```bash
+journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep puppet
+```
+
+#### System without systemd
+
+Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name:
+
+```bash
+grep puppet /var/log/netdata/collector.log
+```
+
+**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues.
+
+#### Docker Container
+
+If your Netdata runs in a Docker container named "netdata" (replace if different), use this command:
+
+```bash
+docker logs netdata 2>&1 | grep puppet
+```
+
+
diff --git a/src/go/plugin/go.d/modules/puppet/metadata.yaml b/src/go/plugin/go.d/modules/puppet/metadata.yaml
new file mode 100644
index 000000000..fa96ea8f2
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/metadata.yaml
@@ -0,0 +1,184 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-puppet
+ plugin_name: go.d.plugin
+ module_name: puppet
+ monitored_instance:
+ name: Puppet
+ link: "https://www.puppet.com/"
+ categories:
+ - data-collection.ci-cd-systems
+ icon_filename: "puppet.svg"
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ""
+ keywords:
+ - puppet
+ most_popular: false
+ overview:
+ data_collection:
+ metrics_description: |
+ This collector monitors Puppet metrics, including JVM heap and non-heap memory, CPU usage, and file descriptors.
+ method_description: |
+ It uses Puppet's metrics API endpoint [/status/v1/services](https://www.puppet.com/docs/puppetserver/5.3/status-api/v1/services.html) to gather the metrics.
+ supported_platforms:
+ include: []
+ exclude: []
+ multi_instance: true
+ additional_permissions:
+ description: ""
+ default_behavior:
+ auto_detection:
+ description: |
+ By default, it detects Puppet instances running on localhost that are listening on port 8140.
+ On startup, it tries to collect metrics from:
+
+ - https://127.0.0.1:8140
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ setup:
+ prerequisites:
+ list: []
+ configuration:
+ file:
+ name: "go.d/puppet.conf"
+ options:
+ description: |
+ The following options can be defined globally: update_every, autodetection_retry.
+ folding:
+ title: ""
+ enabled: true
+ list:
+ - name: url
+ description: The base URL where the Puppet instance can be accessed.
+ default_value: https://127.0.0.1:8140
+ required: true
+ - name: timeout
+ description: HTTPS request timeout.
+ default_value: 1
+ required: false
+ - name: username
+ description: Username for basic HTTPS authentication.
+ default_value: ""
+ required: false
+ - name: password
+ description: Password for basic HTTPS authentication.
+ default_value: ""
+ required: false
+ - name: proxy_url
+ description: Proxy URL.
+ default_value: ""
+ required: false
+ - name: proxy_username
+ description: Username for proxy basic HTTPS authentication.
+ default_value: ""
+ required: false
+ - name: proxy_password
+ description: Password for proxy basic HTTPS authentication.
+ default_value: ""
+ required: false
+ - name: method
+ description: HTTPS request method.
+ default_value: POST
+ required: false
+ - name: body
+ description: HTTPS request body.
+ default_value: ""
+ required: false
+ - name: headers
+ description: HTTPS request headers.
+ default_value: ""
+ required: false
+ - name: not_follow_redirects
+ description: Redirect handling policy. Controls whether the client follows redirects.
+ default_value: false
+ required: false
+ - name: tls_skip_verify
+ description: Server certificate chain and hostname validation policy. Controls whether the client performs this check.
+ default_value: false
+ required: false
+ - name: tls_ca
+ description: Certification authority that the client uses when verifying the server's certificates.
+ default_value: ""
+ required: false
+ - name: tls_cert
+ description: Client TLS certificate.
+ default_value: ""
+ required: false
+ - name: tls_key
+ description: Client TLS key.
+ default_value: ""
+ required: false
+ examples:
+ folding:
+ enabled: true
+ title: "Config"
+ list:
+ - name: Basic with self-signed certificate
+ description: Puppet with self-signed TLS certificate.
+ config: |
+ jobs:
+ - name: local
+ url: https://127.0.0.1:8140
+ tls_skip_verify: yes
+ - name: Multi-instance
+ description: |
+ > **Note**: When you define multiple jobs, their names must be unique.
+
+ Collecting metrics from local and remote instances.
+ config: |
+ jobs:
+ - name: local
+ url: https://127.0.0.1:8140
+ tls_skip_verify: yes
+
+ - name: remote
+ url: https://192.0.2.1:8140
+ tls_skip_verify: yes
+ troubleshooting:
+ problems:
+ list: []
+ alerts: []
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: "These metrics refer to the entire monitored application."
+ labels: []
+ metrics:
+ - name: puppet.jvm_heap
+ description: JVM Heap
+ unit: "MiB"
+ chart_type: area
+ dimensions:
+ - name: committed
+ - name: used
+ - name: puppet.jvm_nonheap
+ description: JVM Non-Heap
+ unit: "MiB"
+ chart_type: area
+ dimensions:
+ - name: committed
+ - name: used
+ - name: puppet.cpu
+ description: CPU usage
+ unit: "percentage"
+ chart_type: stacked
+ dimensions:
+ - name: execution
+ - name: GC
+ - name: puppet.fdopen
+ description: File Descriptors
+ unit: "descriptors"
+ chart_type: line
+ dimensions:
+ - name: used
diff --git a/src/go/plugin/go.d/modules/puppet/puppet.go b/src/go/plugin/go.d/modules/puppet/puppet.go
new file mode 100644
index 000000000..e6eb7b058
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/puppet.go
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package puppet
+
+import (
+ _ "embed"
+ "errors"
+ "net/http"
+ "time"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("puppet", module.Creator{
+ JobConfigSchema: configSchema,
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *Puppet {
+ return &Puppet{
+ Config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{
+ URL: "https://127.0.0.1:8140",
+ },
+ Client: web.Client{
+ Timeout: web.Duration(time.Second * 1),
+ },
+ },
+ },
+ charts: charts.Copy(),
+ }
+}
+
+type Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ web.HTTP `yaml:",inline" json:""`
+}
+
+type Puppet struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ charts *module.Charts
+
+ httpClient *http.Client
+}
+
+func (p *Puppet) Configuration() any {
+ return p.Config
+}
+
+func (p *Puppet) Init() error {
+ if p.URL == "" {
+ p.Error("URL not set")
+ return errors.New("url not set")
+ }
+
+ client, err := web.NewHTTPClient(p.Client)
+ if err != nil {
+ p.Error(err)
+ return err
+ }
+ p.httpClient = client
+
+ p.Debugf("using URL %s", p.URL)
+ p.Debugf("using timeout: %s", p.Timeout)
+
+ return nil
+}
+
+func (p *Puppet) Check() error {
+ mx, err := p.collect()
+ if err != nil {
+ p.Error(err)
+ return err
+ }
+
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+ }
+
+ return nil
+}
+
+func (p *Puppet) Charts() *module.Charts {
+ return p.charts
+}
+
+func (p *Puppet) Collect() map[string]int64 {
+ mx, err := p.collect()
+ if err != nil {
+ p.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+
+ return mx
+}
+
+func (p *Puppet) Cleanup() {
+ if p.httpClient != nil {
+ p.httpClient.CloseIdleConnections()
+ }
+}
diff --git a/src/go/plugin/go.d/modules/puppet/puppet_test.go b/src/go/plugin/go.d/modules/puppet/puppet_test.go
new file mode 100644
index 000000000..7c80a638a
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/puppet_test.go
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package puppet
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+
+ serviceStatusResponse, _ = os.ReadFile("testdata/serviceStatusResponse.json")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "serviceStatusResponse": serviceStatusResponse,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestPuppet_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &Puppet{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestPuppet_Init(t *testing.T) {
+ tests := map[string]struct {
+ wantFail bool
+ config Config
+ }{
+ "success with default": {
+ wantFail: false,
+ config: New().Config,
+ },
+ "fail when URL not set": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{URL: ""},
+ },
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ puppet := New()
+ puppet.Config = test.config
+
+ if test.wantFail {
+ assert.Error(t, puppet.Init())
+ } else {
+ assert.NoError(t, puppet.Init())
+ }
+ })
+ }
+}
+
+func TestPuppet_Charts(t *testing.T) {
+ assert.NotNil(t, New().Charts())
+}
+
+func TestPuppet_Check(t *testing.T) {
+ tests := map[string]struct {
+ wantFail bool
+ prepare func(t *testing.T) (*Puppet, func())
+ }{
+ "success default config": {
+ wantFail: false,
+ prepare: prepareCaseOkDefault,
+ },
+ "fails on unexpected json response": {
+ wantFail: true,
+ prepare: prepareCaseUnexpectedJsonResponse,
+ },
+ "fails on invalid format response": {
+ wantFail: true,
+ prepare: prepareCaseInvalidFormatResponse,
+ },
+ "fails on connection refused": {
+ wantFail: true,
+ prepare: prepareCaseConnectionRefused,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ puppet, cleanup := test.prepare(t)
+ defer cleanup()
+
+ if test.wantFail {
+ assert.Error(t, puppet.Check())
+ } else {
+ assert.NoError(t, puppet.Check())
+ }
+ })
+ }
+}
+
+func TestPuppet_Collect(t *testing.T) {
+ tests := map[string]struct {
+ prepare func(t *testing.T) (*Puppet, func())
+ wantMetrics map[string]int64
+ }{
+ "success default config": {
+ prepare: prepareCaseOkDefault,
+ wantMetrics: map[string]int64{
+ "cpu_usage": 49,
+ "fd_max": 524288,
+ "fd_used": 234,
+ "gc_cpu_usage": 0,
+ "jvm_heap_committed": 1073741824,
+ "jvm_heap_init": 1073741824,
+ "jvm_heap_max": 1073741824,
+ "jvm_heap_used": 550502400,
+ "jvm_nonheap_committed": 334102528,
+ "jvm_nonheap_init": 7667712,
+ "jvm_nonheap_max": -1,
+ "jvm_nonheap_used": 291591160,
+ },
+ },
+ "fails on unexpected json response": {
+ prepare: prepareCaseUnexpectedJsonResponse,
+ },
+ "fails on invalid format response": {
+ prepare: prepareCaseInvalidFormatResponse,
+ },
+ "fails on connection refused": {
+ prepare: prepareCaseConnectionRefused,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ puppet, cleanup := test.prepare(t)
+ defer cleanup()
+
+ mx := puppet.Collect()
+
+ require.Equal(t, test.wantMetrics, mx)
+ if len(test.wantMetrics) > 0 {
+ testMetricsHasAllChartsDims(t, puppet, mx)
+ }
+ })
+ }
+}
+
+func testMetricsHasAllChartsDims(t *testing.T, puppet *Puppet, mx map[string]int64) {
+ for _, chart := range *puppet.Charts() {
+ if chart.Obsolete {
+ continue
+ }
+ for _, dim := range chart.Dims {
+ _, ok := mx[dim.ID]
+ assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
+ }
+ for _, v := range chart.Vars {
+ _, ok := mx[v.ID]
+ assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID)
+ }
+ }
+}
+
+func prepareCaseOkDefault(t *testing.T) (*Puppet, func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/status/v1/services":
+ if r.URL.RawQuery != urlQueryStatusService {
+ w.WriteHeader(http.StatusNotFound)
+ } else {
+ _, _ = w.Write(serviceStatusResponse)
+ }
+ default:
+ w.WriteHeader(http.StatusNotFound)
+ }
+ }))
+
+ puppet := New()
+ puppet.URL = srv.URL
+ require.NoError(t, puppet.Init())
+
+ return puppet, srv.Close
+}
+
+func prepareCaseUnexpectedJsonResponse(t *testing.T) (*Puppet, func()) {
+ t.Helper()
+ resp := `
+{
+ "elephant": {
+ "burn": false,
+ "mountain": true,
+ "fog": false,
+ "skin": -1561907625,
+ "burst": "anyway",
+ "shadow": 1558616893
+ },
+ "start": "ever",
+ "base": 2093056027,
+ "mission": -2007590351,
+ "victory": 999053756,
+ "die": false
+}
+`
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write([]byte(resp))
+ }))
+
+ puppet := New()
+ puppet.URL = srv.URL
+ require.NoError(t, puppet.Init())
+
+ return puppet, srv.Close
+}
+
+func prepareCaseInvalidFormatResponse(t *testing.T) (*Puppet, func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write([]byte("hello and\n goodbye"))
+ }))
+
+ puppet := New()
+ puppet.URL = srv.URL
+ require.NoError(t, puppet.Init())
+
+ return puppet, srv.Close
+}
+
+func prepareCaseConnectionRefused(t *testing.T) (*Puppet, func()) {
+ t.Helper()
+ puppet := New()
+ puppet.URL = "http://127.0.0.1:65001"
+ require.NoError(t, puppet.Init())
+
+ return puppet, func() {}
+}
diff --git a/src/go/plugin/go.d/modules/puppet/response.go b/src/go/plugin/go.d/modules/puppet/response.go
new file mode 100644
index 000000000..dc903d0a9
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/response.go
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package puppet
+
+type statusServiceResponse struct {
+ StatusService *struct {
+ Status struct {
+ Experimental struct {
+ JVMMetrics *struct {
+ CPUUsage float64 `json:"cpu-usage" stm:"cpu_usage,1000,1"`
+ GCCPUUsage float64 `json:"gc-cpu-usage" stm:"gc_cpu_usage,1000,1"`
+ HeapMemory struct {
+ Committed int64 `json:"committed" stm:"committed"`
+ Init int64 `json:"init" stm:"init"`
+ Max int64 `json:"max" stm:"max"`
+ Used int64 `json:"used" stm:"used"`
+ } `json:"heap-memory" stm:"jvm_heap"`
+ FileDescriptors struct {
+ Used int `json:"used" stm:"used"`
+ Max int `json:"max" stm:"max"`
+ } `json:"file-descriptors" stm:"fd"`
+ NonHeapMemory struct {
+ Committed int64 `json:"committed" stm:"committed"`
+ Init int64 `json:"init" stm:"init"`
+ Max int64 `json:"max" stm:"max"`
+ Used int64 `json:"used" stm:"used"`
+ } `json:"non-heap-memory" stm:"jvm_nonheap"`
+ } `json:"jvm-metrics" stm:""`
+ } `json:"experimental" stm:""`
+ } `json:"status" stm:""`
+ } `json:"status-service" stm:""`
+}
diff --git a/src/go/plugin/go.d/modules/puppet/testdata/config.json b/src/go/plugin/go.d/modules/puppet/testdata/config.json
new file mode 100644
index 000000000..984c3ed6e
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/testdata/config.json
@@ -0,0 +1,20 @@
+{
+ "update_every": 123,
+ "url": "ok",
+ "body": "ok",
+ "method": "ok",
+ "headers": {
+ "ok": "ok"
+ },
+ "username": "ok",
+ "password": "ok",
+ "proxy_url": "ok",
+ "proxy_username": "ok",
+ "proxy_password": "ok",
+ "timeout": 123.123,
+ "not_follow_redirects": true,
+ "tls_ca": "ok",
+ "tls_cert": "ok",
+ "tls_key": "ok",
+ "tls_skip_verify": true
+}
diff --git a/src/go/plugin/go.d/modules/puppet/testdata/config.yaml b/src/go/plugin/go.d/modules/puppet/testdata/config.yaml
new file mode 100644
index 000000000..8558b61cc
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/testdata/config.yaml
@@ -0,0 +1,17 @@
+update_every: 123
+url: "ok"
+body: "ok"
+method: "ok"
+headers:
+ ok: "ok"
+username: "ok"
+password: "ok"
+proxy_url: "ok"
+proxy_username: "ok"
+proxy_password: "ok"
+timeout: 123.123
+not_follow_redirects: yes
+tls_ca: "ok"
+tls_cert: "ok"
+tls_key: "ok"
+tls_skip_verify: yes
diff --git a/src/go/plugin/go.d/modules/puppet/testdata/serviceStatusResponse.json b/src/go/plugin/go.d/modules/puppet/testdata/serviceStatusResponse.json
new file mode 100644
index 000000000..a0eee8693
--- /dev/null
+++ b/src/go/plugin/go.d/modules/puppet/testdata/serviceStatusResponse.json
@@ -0,0 +1,497 @@
+{
+ "puppet-profiler": {
+ "service_version": "8.4.0",
+ "service_status_version": 1,
+ "detail_level": "debug",
+ "state": "running",
+ "status": {
+ "experimental": {
+ "function-metrics": [],
+ "resource-metrics": [],
+ "catalog-metrics": [],
+ "puppetdb-metrics": [],
+ "inline-metrics": []
+ }
+ },
+ "active_alerts": []
+ },
+ "jruby-metrics": {
+ "service_version": "8.4.0",
+ "service_status_version": 1,
+ "detail_level": "debug",
+ "state": "running",
+ "status": {
+ "experimental": {
+ "jruby-pool-lock-status": {
+ "current-state": ":not-in-use",
+ "last-change-time": "2024-07-05T06:23:20.120Z"
+ },
+ "metrics": {
+ "average-lock-wait-time": 0,
+ "num-free-jrubies": 4,
+ "borrow-count": 0,
+ "average-requested-jrubies": 0.0,
+ "borrow-timeout-count": 0,
+ "return-count": 0,
+ "borrow-timers": {
+ "total": {
+ "count": 0,
+ "mean": 0,
+ "max": 0,
+ "rate": 0.0
+ }
+ },
+ "borrow-retry-count": 0,
+ "borrowed-instances": [],
+ "average-borrow-time": 0,
+ "num-jrubies": 4,
+ "requested-count": 0,
+ "queue-limit-hit-rate": 0.0,
+ "average-lock-held-time": 0,
+ "requested-instances": [],
+ "queue-limit-hit-count": 0,
+ "average-free-jrubies": 3.3019592583652217,
+ "num-pool-locks": 0,
+ "average-wait-time": 0
+ }
+ }
+ },
+ "active_alerts": []
+ },
+ "ca": {
+ "service_version": "8.4.0",
+ "service_status_version": 1,
+ "detail_level": "debug",
+ "state": "running",
+ "status": {},
+ "active_alerts": []
+ },
+ "master": {
+ "service_version": "8.4.0",
+ "service_status_version": 1,
+ "detail_level": "debug",
+ "state": "running",
+ "status": {
+ "experimental": {
+ "http-metrics": [
+ {
+ "route-id": "puppet-v3-static_file_content-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_content-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environments",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-tasks-:module-name-:task-name",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_metadata-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-facts-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "other",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-tasks",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-compile",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-report-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-node-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-catalog-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-plans-:module-name-:plan-name",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_metadatas-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_bucket_file-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v4-catalog",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "total",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environment_modules-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environment_classes-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-plans",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environment_transports-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ }
+ ],
+ "http-client-metrics": []
+ }
+ },
+ "active_alerts": []
+ },
+ "server": {
+ "service_version": "8.4.0",
+ "service_status_version": 1,
+ "detail_level": "debug",
+ "state": "running",
+ "status": {
+ "experimental": {
+ "http-metrics": [
+ {
+ "route-id": "puppet-v3-static_file_content-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_content-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environments",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-tasks-:module-name-:task-name",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_metadata-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-facts-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "other",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-tasks",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-compile",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-report-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-node-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-catalog-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-plans-:module-name-:plan-name",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_metadatas-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-file_bucket_file-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v4-catalog",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "total",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environment_modules-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environment_classes-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-plans",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ },
+ {
+ "route-id": "puppet-v3-environment_transports-/*/",
+ "count": 0,
+ "mean": 0,
+ "aggregate": 0
+ }
+ ],
+ "http-client-metrics": []
+ }
+ },
+ "active_alerts": []
+ },
+ "status-service": {
+ "service_version": "1.1.1",
+ "service_status_version": 1,
+ "detail_level": "debug",
+ "state": "running",
+ "status": {
+ "experimental": {
+ "jvm-metrics": {
+ "cpu-usage": 0.04997002,
+ "up-time-ms": 51328,
+ "memory-pools": {
+ "Metaspace": {
+ "type": "NON_HEAP",
+ "usage": {
+ "committed": 214106112,
+ "init": 0,
+ "max": -1,
+ "used": 183450600
+ }
+ },
+ "CodeHeap 'non-nmethods'": {
+ "type": "NON_HEAP",
+ "usage": {
+ "committed": 2555904,
+ "init": 2555904,
+ "max": 5840896,
+ "used": 1923072
+ }
+ },
+ "CodeHeap 'profiled nmethods'": {
+ "type": "NON_HEAP",
+ "usage": {
+ "committed": 52559872,
+ "init": 2555904,
+ "max": 122908672,
+ "used": 52545664
+ }
+ },
+ "Compressed Class Space": {
+ "type": "NON_HEAP",
+ "usage": {
+ "committed": 49020928,
+ "init": 0,
+ "max": 1073741824,
+ "used": 37887856
+ }
+ },
+ "G1 Eden Space": {
+ "type": "HEAP",
+ "usage": {
+ "committed": 542113792,
+ "init": 53477376,
+ "max": -1,
+ "used": 146800640
+ }
+ },
+ "G1 Old Gen": {
+ "type": "HEAP",
+ "usage": {
+ "committed": 462422016,
+ "init": 1020264448,
+ "max": 1073741824,
+ "used": 335020032
+ }
+ },
+ "G1 Survivor Space": {
+ "type": "HEAP",
+ "usage": {
+ "committed": 69206016,
+ "init": 0,
+ "max": -1,
+ "used": 68681728
+ }
+ },
+ "CodeHeap 'non-profiled nmethods'": {
+ "type": "NON_HEAP",
+ "usage": {
+ "committed": 15597568,
+ "init": 2555904,
+ "max": 122908672,
+ "used": 15588736
+ }
+ }
+ },
+ "gc-cpu-usage": 0.0,
+ "threading": {
+ "thread-count": 59,
+ "peak-thread-count": 59
+ },
+ "heap-memory": {
+ "committed": 1073741824,
+ "init": 1073741824,
+ "max": 1073741824,
+ "used": 550502400
+ },
+ "gc-stats": {
+ "G1 Young Generation": {
+ "count": 18,
+ "total-time-ms": 550,
+ "last-gc-info": {
+ "duration-ms": 75
+ }
+ },
+ "G1 Old Generation": {
+ "count": 0,
+ "total-time-ms": 0
+ },
+ "G1 Concurrent GC": {
+ "count": 10,
+ "total-time-ms": 49,
+ "last-gc-info": {
+ "duration-ms": 0
+ }
+ }
+ },
+ "start-time-ms": 1720160584298,
+ "file-descriptors": {
+ "used": 234,
+ "max": 524288
+ },
+ "non-heap-memory": {
+ "committed": 334102528,
+ "init": 7667712,
+ "max": -1,
+ "used": 291591160
+ },
+ "nio-buffer-pools": {
+ "mapped": {
+ "count": 0,
+ "memory-used": 0,
+ "total-capacity": 0
+ },
+ "direct": {
+ "count": 11,
+ "memory-used": 197631,
+ "total-capacity": 197631
+ },
+ "mapped - 'non-volatile memory'": {
+ "count": 0,
+ "memory-used": 0,
+ "total-capacity": 0
+ }
+ }
+ }
+ }
+ },
+ "active_alerts": []
+ }
+}