From 753c5930b77dbc91caaae6b2df72697af00f4333 Mon Sep 17 00:00:00 2001 From: Dewald de Jager Date: Tue, 1 Aug 2023 03:41:40 +0200 Subject: [PATCH] [receiver/sshcheck] Include SSH endpoint resource attribute (#24676) **Description:** The metadata.yml for the SSH check receiver currently documents a resource attribute containing the SSH endpoint but this is not emitted. This PR updates the receiver to include this resource attribute. **Link to tracking Issue:** #24441 **Testing:** Example collector config: ```yaml receivers: sshcheck: endpoint: 13.245.150.131:22 username: ec2-user key_file: /Users/dewald.dejager/.ssh/sandbox.pem collection_interval: 15s known_hosts: /Users/dewald.dejager/.ssh/known_hosts ignore_host_key: false resource_attributes: "ssh.endpoint": enabled: true exporters: logging: verbosity: detailed prometheus: endpoint: 0.0.0.0:8081 resource_to_telemetry_conversion: enabled: true service: pipelines: metrics: receivers: [sshcheck] exporters: [logging, prometheus] ``` The log output looks like this: ``` 2023-07-30T16:52:38.724+0200 info MetricsExporter {"kind": "exporter", "data_type": "metrics", "name": "logging", "resource metrics": 1, "metrics": 2, "data points": 2} 2023-07-30T16:52:38.724+0200 info ResourceMetrics #0 Resource SchemaURL: Resource attributes: -> ssh.endpoint: Str(13.245.150.131:22) ScopeMetrics #0 ScopeMetrics SchemaURL: InstrumentationScope otelcol/sshcheckreceiver 0.82.0-dev Metric #0 Descriptor: -> Name: sshcheck.duration -> Description: Measures the duration of SSH connection. -> Unit: ms -> DataType: Gauge NumberDataPoints #0 StartTimestamp: 2023-07-30 14:52:22.381672 +0000 UTC Timestamp: 2023-07-30 14:52:38.404003 +0000 UTC Value: 319 Metric #1 Descriptor: -> Name: sshcheck.status -> Description: 1 if the SSH client successfully connected, otherwise 0. -> Unit: 1 -> DataType: Sum -> IsMonotonic: false -> AggregationTemporality: Cumulative NumberDataPoints #0 StartTimestamp: 2023-07-30 14:52:22.381672 +0000 UTC Timestamp: 2023-07-30 14:52:38.404003 +0000 UTC Value: 1 ``` And the Prometheus metrics look like this: ``` # HELP sshcheck_duration Measures the duration of SSH connection. # TYPE sshcheck_duration gauge sshcheck_duration{ssh_endpoint="13.245.150.131:22"} 311 # HELP sshcheck_status 1 if the SSH client successfully connected, otherwise 0. # TYPE sshcheck_status gauge sshcheck_status{ssh_endpoint="13.245.150.131:22"} 1 ``` --- .../add-ssh-endpoint-resource-attribute.yaml | 20 +++++++++++ receiver/sshcheckreceiver/scraper.go | 5 ++- receiver/sshcheckreceiver/scraper_test.go | 36 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 .chloggen/add-ssh-endpoint-resource-attribute.yaml diff --git a/.chloggen/add-ssh-endpoint-resource-attribute.yaml b/.chloggen/add-ssh-endpoint-resource-attribute.yaml new file mode 100644 index 000000000000..632723f70fa2 --- /dev/null +++ b/.chloggen/add-ssh-endpoint-resource-attribute.yaml @@ -0,0 +1,20 @@ +# Use this changelog template to create an entry for release notes. +# If your change doesn't affect end users, such as a test fix or a tooling change, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: receiver/sshcheck + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add the SSH endpoint as a resource attribute + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [24441] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/receiver/sshcheckreceiver/scraper.go b/receiver/sshcheckreceiver/scraper.go index 25131dc367ad..bfc95407c944 100644 --- a/receiver/sshcheckreceiver/scraper.go +++ b/receiver/sshcheckreceiver/scraper.go @@ -24,6 +24,7 @@ type sshcheckScraper struct { *configssh.Client *Config settings component.TelemetrySettings + rb *metadata.ResourceBuilder mb *metadata.MetricsBuilder } @@ -124,13 +125,15 @@ func (s *sshcheckScraper) scrape(ctx context.Context) (_ pmetric.Metrics, err er } } - return s.mb.Emit(), nil + s.rb.SetSSHEndpoint(s.Config.SSHClientSettings.Endpoint) + return s.mb.Emit(metadata.WithResource(s.rb.Emit())), nil } func newScraper(conf *Config, settings receiver.CreateSettings) *sshcheckScraper { return &sshcheckScraper{ Config: conf, settings: settings.TelemetrySettings, + rb: metadata.NewResourceBuilder(conf.MetricsBuilderConfig.ResourceAttributes), mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings), } } diff --git a/receiver/sshcheckreceiver/scraper_test.go b/receiver/sshcheckreceiver/scraper_test.go index 43dd3789e7fe..81f6deb5e9e8 100644 --- a/receiver/sshcheckreceiver/scraper_test.go +++ b/receiver/sshcheckreceiver/scraper_test.go @@ -183,6 +183,42 @@ func TestScraper(t *testing.T) { } } +func TestScraperPropagatesResourceAttributes(t *testing.T) { + if !supportedOS() { + t.Skip("Skip tests if not running on one of: [linux, darwin, freebsd, openbsd]") + } + endpoint := setupSSHServer(t) + require.NotEmpty(t, endpoint) + + f := NewFactory() + cfg := f.CreateDefaultConfig().(*Config) + cfg.MetricsBuilderConfig.ResourceAttributes.SSHEndpoint.Enabled = true + cfg.ScraperControllerSettings.CollectionInterval = 100 * time.Millisecond + cfg.Username = "otelu" + cfg.Password = "otelp" + cfg.Endpoint = endpoint + cfg.IgnoreHostKey = true + + settings := receivertest.NewNopCreateSettings() + + scraper := newScraper(cfg, settings) + require.NoError(t, scraper.start(context.Background(), componenttest.NewNopHost()), "failed starting scraper") + + actualMetrics, err := scraper.scrape(context.Background()) + require.NoError(t, err, "failed scrape") + + resourceMetrics := actualMetrics.ResourceMetrics() + expectedResourceAttributes := map[string]any{"ssh.endpoint": endpoint} + for i := 0; i < resourceMetrics.Len(); i++ { + resourceAttributes := resourceMetrics.At(i).Resource().Attributes() + for name, value := range expectedResourceAttributes { + actualAttributeValue, ok := resourceAttributes.Get(name) + require.True(t, ok) + require.Equal(t, value, actualAttributeValue.Str()) + } + } +} + func TestScraperDoesNotErrForSSHErr(t *testing.T) { if !supportedOS() { t.Skip("Skip tests if not running on one of: [linux, darwin, freebsd, openbsd]")