From a1d7a33d9381d2b530337ccc6572b223373b138c Mon Sep 17 00:00:00 2001 From: Michael Burt Date: Wed, 9 Oct 2024 16:29:52 -0600 Subject: [PATCH] [receiver/tlscheck] Inital Commit of TLS Check Receiver (#35441) **Description:** Initial commit of TLS Check Receiver, a new component aimed at emitting metrics about the expoiry of x.509 certs. **Link to tracking Issue:** [35423](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35423) **Testing:** Test files added **Documentation:** `README.md` & `metadata.yaml` added --- .chloggen/tlscheckreceiver-addition.yaml | 27 +++ .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/feature_request.yaml | 1 + .github/ISSUE_TEMPLATE/other.yaml | 1 + .github/ISSUE_TEMPLATE/unmaintained.yaml | 1 + receiver/tlscheckreceiver/Makefile | 1 + receiver/tlscheckreceiver/README.md | 37 +++ receiver/tlscheckreceiver/config.go | 63 +++++ receiver/tlscheckreceiver/config_test.go | 95 ++++++++ receiver/tlscheckreceiver/doc.go | 6 + receiver/tlscheckreceiver/documentation.md | 34 +++ receiver/tlscheckreceiver/factory.go | 62 +++++ receiver/tlscheckreceiver/factory_test.go | 81 +++++++ .../generated_component_test.go | 69 ++++++ .../generated_package_test.go | 13 + receiver/tlscheckreceiver/go.mod | 55 +++++ receiver/tlscheckreceiver/go.sum | 136 +++++++++++ .../internal/metadata/generated_config.go | 92 +++++++ .../metadata/generated_config_test.go | 109 +++++++++ .../internal/metadata/generated_metrics.go | 227 ++++++++++++++++++ .../metadata/generated_metrics_test.go | 121 ++++++++++ .../internal/metadata/generated_resource.go | 36 +++ .../metadata/generated_resource_test.go | 40 +++ .../internal/metadata/generated_status.go | 16 ++ .../internal/metadata/testdata/config.yaml | 27 +++ receiver/tlscheckreceiver/metadata.yaml | 35 +++ receiver/tlscheckreceiver/scraper.go | 31 +++ reports/distributions/contrib.yaml | 1 + versions.yaml | 1 + 30 files changed, 1420 insertions(+) create mode 100644 .chloggen/tlscheckreceiver-addition.yaml create mode 100644 receiver/tlscheckreceiver/Makefile create mode 100644 receiver/tlscheckreceiver/README.md create mode 100644 receiver/tlscheckreceiver/config.go create mode 100644 receiver/tlscheckreceiver/config_test.go create mode 100644 receiver/tlscheckreceiver/doc.go create mode 100644 receiver/tlscheckreceiver/documentation.md create mode 100644 receiver/tlscheckreceiver/factory.go create mode 100644 receiver/tlscheckreceiver/factory_test.go create mode 100644 receiver/tlscheckreceiver/generated_component_test.go create mode 100644 receiver/tlscheckreceiver/generated_package_test.go create mode 100644 receiver/tlscheckreceiver/go.mod create mode 100644 receiver/tlscheckreceiver/go.sum create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_config.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_config_test.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_metrics.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_metrics_test.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_resource.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_resource_test.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/generated_status.go create mode 100644 receiver/tlscheckreceiver/internal/metadata/testdata/config.yaml create mode 100644 receiver/tlscheckreceiver/metadata.yaml create mode 100644 receiver/tlscheckreceiver/scraper.go diff --git a/.chloggen/tlscheckreceiver-addition.yaml b/.chloggen/tlscheckreceiver-addition.yaml new file mode 100644 index 000000000000..19ae64d6134d --- /dev/null +++ b/.chloggen/tlscheckreceiver-addition.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: new_component + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: tlscheckreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add TLS Check Receiver component to monitor x.509 certificate expiry + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35423] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dfc9746f744a..587df26bad20 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -295,6 +295,7 @@ receiver/sshcheckreceiver/ @open-teleme receiver/statsdreceiver/ @open-telemetry/collector-contrib-approvers @jmacd @dmitryax receiver/syslogreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski @andrzej-stencel receiver/tcplogreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski +receiver/tlscheckreceiver/ @open-telemetry/collector-contrib-approvers @atoulme @michael-burt receiver/udplogreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski receiver/vcenterreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski @schmikei @StefanKurek receiver/wavefrontreceiver/ @open-telemetry/collector-contrib-approvers @samiura diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index b705cbecfb53..414d3b55dd18 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -290,6 +290,7 @@ body: - receiver/statsd - receiver/syslog - receiver/tcplog + - receiver/tlscheck - receiver/udplog - receiver/vcenter - receiver/wavefront diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 8ec681fa0a67..e7735b4741c0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -284,6 +284,7 @@ body: - receiver/statsd - receiver/syslog - receiver/tcplog + - receiver/tlscheck - receiver/udplog - receiver/vcenter - receiver/wavefront diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml index b0098510b798..cf05bfa8a075 100644 --- a/.github/ISSUE_TEMPLATE/other.yaml +++ b/.github/ISSUE_TEMPLATE/other.yaml @@ -284,6 +284,7 @@ body: - receiver/statsd - receiver/syslog - receiver/tcplog + - receiver/tlscheck - receiver/udplog - receiver/vcenter - receiver/wavefront diff --git a/.github/ISSUE_TEMPLATE/unmaintained.yaml b/.github/ISSUE_TEMPLATE/unmaintained.yaml index df65436ba755..ef8383e2d661 100644 --- a/.github/ISSUE_TEMPLATE/unmaintained.yaml +++ b/.github/ISSUE_TEMPLATE/unmaintained.yaml @@ -289,6 +289,7 @@ body: - receiver/statsd - receiver/syslog - receiver/tcplog + - receiver/tlscheck - receiver/udplog - receiver/vcenter - receiver/wavefront diff --git a/receiver/tlscheckreceiver/Makefile b/receiver/tlscheckreceiver/Makefile new file mode 100644 index 000000000000..c1496226e590 --- /dev/null +++ b/receiver/tlscheckreceiver/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common \ No newline at end of file diff --git a/receiver/tlscheckreceiver/README.md b/receiver/tlscheckreceiver/README.md new file mode 100644 index 000000000000..1a5a89bf0317 --- /dev/null +++ b/receiver/tlscheckreceiver/README.md @@ -0,0 +1,37 @@ +# TLS Check Receiver + + +| Status | | +| ------------- |-----------| +| Stability | [development]: metrics | +| Distributions | [contrib] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Ftlscheck%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Ftlscheck) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Ftlscheck%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Ftlscheck) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@atoulme](https://www.github.com/atoulme), [@michael-burt](https://www.github.com/michael-burt) | + +[development]: https://github.com/open-telemetry/opentelemetry-collector#development +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib + + +Emit metrics about x.509 certificates. + +## Getting Started + +By default, the TLS Check Receiver will emit a single metric, `tlscheck.time_left`, per target. This is measured in seconds until the date and time specified in the `NotAfter` field of the x.509 certificate. After certificate expiration, the metric value will be a negative integer measuring the time in seconds since expiry. + +## Example Configuration + +```yaml +receivers: + tlscheck: + targets: + - url: https://example.com + - url: https://foobar.com:8080 +``` + +## Certificate Verification + +This component does not provide hostname, validity period, path, or CRL / OCSP verification on the certificate. + +## Metrics + +Details about the metrics produced by this receiver can be found in [metadata.yaml](./metadata.yaml). diff --git a/receiver/tlscheckreceiver/config.go b/receiver/tlscheckreceiver/config.go new file mode 100644 index 000000000000..d20c7b2e091d --- /dev/null +++ b/receiver/tlscheckreceiver/config.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" + +import ( + "errors" + "fmt" + "net/url" + + "go.opentelemetry.io/collector/receiver/scraperhelper" + "go.uber.org/multierr" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver/internal/metadata" +) + +// Predefined error responses for configuration validation failures +var ( + errMissingURL = errors.New(`"url" must be specified`) + errInvalidURL = errors.New(`"url" must be in the form of ://[:]`) +) + +// Config defines the configuration for the various elements of the receiver agent. +type Config struct { + scraperhelper.ControllerConfig `mapstructure:",squash"` + metadata.MetricsBuilderConfig `mapstructure:",squash"` + Targets []*targetConfig `mapstructure:"targets"` +} + +type targetConfig struct { + URL string `mapstructure:"url"` +} + +// Validate validates the configuration by checking for missing or invalid fields +func (cfg *targetConfig) Validate() error { + var err error + + if cfg.URL == "" { + err = multierr.Append(err, errMissingURL) + } else { + _, parseErr := url.ParseRequestURI(cfg.URL) + if parseErr != nil { + err = multierr.Append(err, fmt.Errorf("%s: %w", errInvalidURL.Error(), parseErr)) + } + } + + return err +} + +// Validate validates the configuration by checking for missing or invalid fields +func (cfg *Config) Validate() error { + var err error + + if len(cfg.Targets) == 0 { + err = multierr.Append(err, errMissingURL) + } + + for _, target := range cfg.Targets { + err = multierr.Append(err, target.Validate()) + } + + return err +} diff --git a/receiver/tlscheckreceiver/config_test.go b/receiver/tlscheckreceiver/config_test.go new file mode 100644 index 000000000000..54e1748352c9 --- /dev/null +++ b/receiver/tlscheckreceiver/config_test.go @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/receiver/scraperhelper" +) + +func TestValidate(t *testing.T) { + testCases := []struct { + desc string + cfg *Config + expectedErr error + }{ + { + desc: "missing url", + cfg: &Config{ + Targets: []*targetConfig{}, + ControllerConfig: scraperhelper.NewDefaultControllerConfig(), + }, + expectedErr: errMissingURL, + }, + { + desc: "invalid url", + cfg: &Config{ + Targets: []*targetConfig{ + { + URL: "invalid://endpoint: 12efg", + }, + }, + ControllerConfig: scraperhelper.NewDefaultControllerConfig(), + }, + expectedErr: fmt.Errorf("%w: %s", errInvalidURL, `parse "invalid://endpoint: 12efg": invalid port ": 12efg" after host`), + }, + { + desc: "invalid config with multiple targets", + cfg: &Config{ + Targets: []*targetConfig{ + { + URL: "invalid://endpoint: 12efg", + }, + { + URL: "https://example.com", + }, + }, + ControllerConfig: scraperhelper.NewDefaultControllerConfig(), + }, + expectedErr: fmt.Errorf("%w: %s", errInvalidURL, `parse "invalid://endpoint: 12efg": invalid port ": 12efg" after host`), + }, + { + desc: "missing scheme", + cfg: &Config{ + Targets: []*targetConfig{ + { + URL: "www.opentelemetry.io/docs", + }, + }, + ControllerConfig: scraperhelper.NewDefaultControllerConfig(), + }, + expectedErr: fmt.Errorf("%w: %s", errInvalidURL, `parse "www.opentelemetry.io/docs": invalid URI for request`), + }, + { + desc: "valid config", + cfg: &Config{ + Targets: []*targetConfig{ + { + URL: "https://opentelemetry.io", + }, + { + URL: "https://opentelemetry.io:80/docs", + }, + }, + ControllerConfig: scraperhelper.NewDefaultControllerConfig(), + }, + expectedErr: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + actualErr := tc.cfg.Validate() + if tc.expectedErr != nil { + require.EqualError(t, actualErr, tc.expectedErr.Error()) + } else { + require.NoError(t, actualErr) + } + + }) + } +} diff --git a/receiver/tlscheckreceiver/doc.go b/receiver/tlscheckreceiver/doc.go new file mode 100644 index 000000000000..3ff042cd07f6 --- /dev/null +++ b/receiver/tlscheckreceiver/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" diff --git a/receiver/tlscheckreceiver/documentation.md b/receiver/tlscheckreceiver/documentation.md new file mode 100644 index 000000000000..1b0d5e7e5cf6 --- /dev/null +++ b/receiver/tlscheckreceiver/documentation.md @@ -0,0 +1,34 @@ +[comment]: <> (Code generated by mdatagen. DO NOT EDIT.) + +# tlscheck + +## Default Metrics + +The following metrics are emitted by default. Each of them can be disabled by applying the following configuration: + +```yaml +metrics: + : + enabled: false +``` + +### tlscheck.time_left + +Time in seconds until certificate expiry, as specified by `NotAfter` field in the x.509 certificate. Negative values represent time in seconds since expiration. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| s | Gauge | Int | + +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| tlscheck.x509.issuer | The entity that issued the certificate. | Any Str | +| tlscheck.x509.cn | The commonName in the subject of the certificate. | Any Str | + +## Resource Attributes + +| Name | Description | Values | Enabled | +| ---- | ----------- | ------ | ------- | +| tlscheck.url | Url at which the certificate was accessed. | Any Str | true | diff --git a/receiver/tlscheckreceiver/factory.go b/receiver/tlscheckreceiver/factory.go new file mode 100644 index 000000000000..bc99c145abf7 --- /dev/null +++ b/receiver/tlscheckreceiver/factory.go @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" + +import ( + "context" + "errors" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/scraperhelper" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver/internal/metadata" +) + +var ( + errConfigNotTLSCheck = errors.New(`invalid config`) +) + +// NewFactory creates a new filestats receiver factory. +func NewFactory() receiver.Factory { + return receiver.NewFactory( + metadata.Type, + newDefaultConfig, + receiver.WithMetrics(newReceiver, metadata.MetricsStability)) +} + +func newDefaultConfig() component.Config { + return &Config{ + ControllerConfig: scraperhelper.NewDefaultControllerConfig(), + MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + Targets: []*targetConfig{}, + } +} + +func newReceiver( + _ context.Context, + settings receiver.Settings, + cfg component.Config, + consumer consumer.Metrics, +) (receiver.Metrics, error) { + tlsCheckConfig, ok := cfg.(*Config) + if !ok { + return nil, errConfigNotTLSCheck + } + + mp := newScraper(tlsCheckConfig, settings) + s, err := scraperhelper.NewScraper(metadata.Type, mp.scrape) + if err != nil { + return nil, err + } + opt := scraperhelper.AddScraper(s) + + return scraperhelper.NewScraperControllerReceiver( + &tlsCheckConfig.ControllerConfig, + settings, + consumer, + opt, + ) +} diff --git a/receiver/tlscheckreceiver/factory_test.go b/receiver/tlscheckreceiver/factory_test.go new file mode 100644 index 000000000000..2ca0e3d0ff0c --- /dev/null +++ b/receiver/tlscheckreceiver/factory_test.go @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" + "go.opentelemetry.io/collector/receiver/scraperhelper" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver/internal/metadata" +) + +func TestNewFactory(t *testing.T) { + testCases := []struct { + desc string + testFunc func(*testing.T) + }{ + { + desc: "creates a new factory with correct type", + testFunc: func(t *testing.T) { + factory := NewFactory() + require.EqualValues(t, metadata.Type, factory.Type()) + }, + }, + { + desc: "creates a new factory with default config", + testFunc: func(t *testing.T) { + factory := NewFactory() + + var expectedCfg component.Config = &Config{ + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: 60 * time.Second, + InitialDelay: time.Second, + }, + MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + Targets: []*targetConfig{}, + } + + require.Equal(t, expectedCfg, factory.CreateDefaultConfig()) + }, + }, + { + desc: "creates a new factory and CreateMetrics returns no error", + testFunc: func(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig() + _, err := factory.CreateMetrics( + context.Background(), + receivertest.NewNopSettings(), + cfg, + consumertest.NewNop(), + ) + require.NoError(t, err) + }, + }, + { + desc: "creates a new factory and CreateMetrics returns error with incorrect config", + testFunc: func(t *testing.T) { + factory := NewFactory() + _, err := factory.CreateMetrics( + context.Background(), + receivertest.NewNopSettings(), + nil, + consumertest.NewNop(), + ) + require.ErrorIs(t, err, errConfigNotTLSCheck) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, tc.testFunc) + } +} diff --git a/receiver/tlscheckreceiver/generated_component_test.go b/receiver/tlscheckreceiver/generated_component_test.go new file mode 100644 index 000000000000..87b5a4f979fd --- /dev/null +++ b/receiver/tlscheckreceiver/generated_component_test.go @@ -0,0 +1,69 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package tlscheckreceiver + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/confmap/confmaptest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestComponentFactoryType(t *testing.T) { + require.Equal(t, "tlscheck", NewFactory().Type().String()) +} + +func TestComponentConfigStruct(t *testing.T) { + require.NoError(t, componenttest.CheckConfigStruct(NewFactory().CreateDefaultConfig())) +} + +func TestComponentLifecycle(t *testing.T) { + factory := NewFactory() + + tests := []struct { + name string + createFn func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) + }{ + + { + name: "metrics", + createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateMetrics(ctx, set, cfg, consumertest.NewNop()) + }, + }, + } + + cm, err := confmaptest.LoadConf("metadata.yaml") + require.NoError(t, err) + cfg := factory.CreateDefaultConfig() + sub, err := cm.Sub("tests::config") + require.NoError(t, err) + require.NoError(t, sub.Unmarshal(&cfg)) + + for _, tt := range tests { + t.Run(tt.name+"-shutdown", func(t *testing.T) { + c, err := tt.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + t.Run(tt.name+"-lifecycle", func(t *testing.T) { + firstRcvr, err := tt.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + host := componenttest.NewNopHost() + require.NoError(t, err) + require.NoError(t, firstRcvr.Start(context.Background(), host)) + require.NoError(t, firstRcvr.Shutdown(context.Background())) + secondRcvr, err := tt.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + require.NoError(t, secondRcvr.Start(context.Background(), host)) + require.NoError(t, secondRcvr.Shutdown(context.Background())) + }) + } +} diff --git a/receiver/tlscheckreceiver/generated_package_test.go b/receiver/tlscheckreceiver/generated_package_test.go new file mode 100644 index 000000000000..98a559a58039 --- /dev/null +++ b/receiver/tlscheckreceiver/generated_package_test.go @@ -0,0 +1,13 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package tlscheckreceiver + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/receiver/tlscheckreceiver/go.mod b/receiver/tlscheckreceiver/go.mod new file mode 100644 index 000000000000..30e7601ee72a --- /dev/null +++ b/receiver/tlscheckreceiver/go.mod @@ -0,0 +1,55 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver + +go 1.22.0 + +require ( + github.com/google/go-cmp v0.6.0 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/component v0.111.1-0.20241008154146-ea48c09c31ae + go.opentelemetry.io/collector/confmap v1.17.1-0.20241008154146-ea48c09c31ae + go.opentelemetry.io/collector/consumer v0.111.1-0.20241008154146-ea48c09c31ae + go.opentelemetry.io/collector/consumer/consumertest v0.111.1-0.20241008154146-ea48c09c31ae + go.opentelemetry.io/collector/filter v0.111.1-0.20241008154146-ea48c09c31ae + go.opentelemetry.io/collector/pdata v1.17.1-0.20241008154146-ea48c09c31ae + go.opentelemetry.io/collector/receiver v0.111.1-0.20241008154146-ea48c09c31ae + go.uber.org/goleak v1.3.0 + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/providers/confmap v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.111.1-0.20241008154146-ea48c09c31ae // indirect + go.opentelemetry.io/collector/consumer/consumerprofiles v0.111.1-0.20241008154146-ea48c09c31ae // indirect + go.opentelemetry.io/collector/internal/globalsignal v0.111.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.111.1-0.20241008154146-ea48c09c31ae // indirect + go.opentelemetry.io/collector/pipeline v0.111.1-0.20241008154146-ea48c09c31ae // indirect + go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.1-0.20241008154146-ea48c09c31ae // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.30.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/receiver/tlscheckreceiver/go.sum b/receiver/tlscheckreceiver/go.sum new file mode 100644 index 000000000000..968b2bb483d2 --- /dev/null +++ b/receiver/tlscheckreceiver/go.sum @@ -0,0 +1,136 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector/component v0.111.1-0.20241008154146-ea48c09c31ae h1:dXAMqXGJp1vWG7qwS/2sjIyJgmyOSfEOm6Gcmkzp1cQ= +go.opentelemetry.io/collector/component v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:iWUfPxpVwZhkI4v3/Gh5wt4iKyJn4lriPFAug8iLXno= +go.opentelemetry.io/collector/config/configtelemetry v0.111.1-0.20241008154146-ea48c09c31ae h1:NmNYRBSP+IUK9CsU1Q/1eS/tXmYTPMYxmGQsxEprq/s= +go.opentelemetry.io/collector/config/configtelemetry v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:R0MBUxjSMVMIhljuDHWIygzzJWQyZHXXWIgQNxcFwhc= +go.opentelemetry.io/collector/confmap v1.17.1-0.20241008154146-ea48c09c31ae h1:mYgomPNE0dq1SU1OVnMb/Z8Xbj89jBXnjJexz9M71t0= +go.opentelemetry.io/collector/confmap v1.17.1-0.20241008154146-ea48c09c31ae/go.mod h1:GrIZ12P/9DPOuTpe2PIS51a0P/ZM6iKtByVee1Uf3+k= +go.opentelemetry.io/collector/consumer v0.111.1-0.20241008154146-ea48c09c31ae h1:ps86XqQ6kviggnQ7OnJIHDIoaYYinRGtfKKzcvSuplc= +go.opentelemetry.io/collector/consumer v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:G6pdEDo5A92RY9sBTVbdbuv/nFrVJbDKSO6uRzzgaP8= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.111.1-0.20241008154146-ea48c09c31ae h1:wQHN+NHZ7RwBDy3nSR52LTwLLLVvu116XdXA4KYjqHA= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:GK0QMMiRBWl4IhIF/7ZKgzBlR9SdRSpRlqyNInN4ZoU= +go.opentelemetry.io/collector/consumer/consumertest v0.111.1-0.20241008154146-ea48c09c31ae h1:HFj6D19fJYm3KV8QidQmMApmLjzoNkzh8El5OkTGySo= +go.opentelemetry.io/collector/consumer/consumertest v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:UDZRrSgaFAwWO6I34fj0KjabVAuBCAnmizsleyIe3I4= +go.opentelemetry.io/collector/filter v0.111.1-0.20241008154146-ea48c09c31ae h1:fLRV9bU33PJWQ/eZCwzfKdV0I9ljhP84Zoq9+tBhcLU= +go.opentelemetry.io/collector/filter v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:74Acew42eexKiuLu3tVehyMK4b5XJPWXoJyNjK2FM+U= +go.opentelemetry.io/collector/internal/globalsignal v0.111.0 h1:oq0nSD+7K2Q1Fx5d3s6lPRdKZeTL0FEg4sIaR7ZJzIc= +go.opentelemetry.io/collector/internal/globalsignal v0.111.0/go.mod h1:GqMXodPWOxK5uqpX8MaMXC2389y2XJTa5nPwf8FYDK8= +go.opentelemetry.io/collector/pdata v1.17.1-0.20241008154146-ea48c09c31ae h1:PcwZe1RD8tC4SZExhf0f5HqK+ZuWGsowHaBBU4PiUv0= +go.opentelemetry.io/collector/pdata v1.17.1-0.20241008154146-ea48c09c31ae/go.mod h1:Ox1YVLe87cZDB/TL30i4SUz1cA5s6AM6SpFMfY61ICs= +go.opentelemetry.io/collector/pdata/pprofile v0.111.1-0.20241008154146-ea48c09c31ae h1:V6Lp/+A2pei61vmZy8Fwa6j22+wyMQNTFDSe1OVqwWc= +go.opentelemetry.io/collector/pdata/pprofile v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:GRY9BmyYV9myczdT6Q9r+6sK2ICinvzXnrM8+46qpMs= +go.opentelemetry.io/collector/pdata/testdata v0.111.0 h1:Fqyf1NJ0az+HbsvKSCNw8pfa1Y6c4FhZwlMK4ZulG0s= +go.opentelemetry.io/collector/pdata/testdata v0.111.0/go.mod h1:7SypOzbVtRsCkns6Yxa4GztnkVGkk7b9fW24Ow75q5s= +go.opentelemetry.io/collector/pipeline v0.111.1-0.20241008154146-ea48c09c31ae h1:/NNb1rBd/Y42FzIjpLjlRSb7bPANHyI3/3DnPg5p50U= +go.opentelemetry.io/collector/pipeline v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:ZZMU3019geEU283rTW5M/LkcqLqHp/YI2Nl6/Vp68PQ= +go.opentelemetry.io/collector/receiver v0.111.1-0.20241008154146-ea48c09c31ae h1:/CdVIXj9tjTU+5U2D2O/w5T7vYbWF+D6mW9J09GWqis= +go.opentelemetry.io/collector/receiver v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:IB7XEWVIprzSO5Y5RoyPLx3I4ntVGvhOWvoHN1n24IY= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.1-0.20241008154146-ea48c09c31ae h1:zJAd63i80I+8wGgK1OB49hX/MJ5GEeS0aNbxxvr7aks= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.1-0.20241008154146-ea48c09c31ae/go.mod h1:cwpkRCGssE2AxydEzkFC3l611d8+csaDH/7BjKC7nHI= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= +go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/sdk/metric v1.30.0 h1:QJLT8Pe11jyHBHfSAgYH7kEmT24eX792jZO1bo4BXkM= +go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_config.go b/receiver/tlscheckreceiver/internal/metadata/generated_config.go new file mode 100644 index 000000000000..96e738301b15 --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_config.go @@ -0,0 +1,92 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/filter" +) + +// MetricConfig provides common config for a particular metric. +type MetricConfig struct { + Enabled bool `mapstructure:"enabled"` + + enabledSetByUser bool +} + +func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { + if parser == nil { + return nil + } + err := parser.Unmarshal(ms) + if err != nil { + return err + } + ms.enabledSetByUser = parser.IsSet("enabled") + return nil +} + +// MetricsConfig provides config for tlscheck metrics. +type MetricsConfig struct { + TlscheckTimeLeft MetricConfig `mapstructure:"tlscheck.time_left"` +} + +func DefaultMetricsConfig() MetricsConfig { + return MetricsConfig{ + TlscheckTimeLeft: MetricConfig{ + Enabled: true, + }, + } +} + +// ResourceAttributeConfig provides common config for a particular resource attribute. +type ResourceAttributeConfig struct { + Enabled bool `mapstructure:"enabled"` + // Experimental: MetricsInclude defines a list of filters for attribute values. + // If the list is not empty, only metrics with matching resource attribute values will be emitted. + MetricsInclude []filter.Config `mapstructure:"metrics_include"` + // Experimental: MetricsExclude defines a list of filters for attribute values. + // If the list is not empty, metrics with matching resource attribute values will not be emitted. + // MetricsInclude has higher priority than MetricsExclude. + MetricsExclude []filter.Config `mapstructure:"metrics_exclude"` + + enabledSetByUser bool +} + +func (rac *ResourceAttributeConfig) Unmarshal(parser *confmap.Conf) error { + if parser == nil { + return nil + } + err := parser.Unmarshal(rac) + if err != nil { + return err + } + rac.enabledSetByUser = parser.IsSet("enabled") + return nil +} + +// ResourceAttributesConfig provides config for tlscheck resource attributes. +type ResourceAttributesConfig struct { + TlscheckURL ResourceAttributeConfig `mapstructure:"tlscheck.url"` +} + +func DefaultResourceAttributesConfig() ResourceAttributesConfig { + return ResourceAttributesConfig{ + TlscheckURL: ResourceAttributeConfig{ + Enabled: true, + }, + } +} + +// MetricsBuilderConfig is a configuration for tlscheck metrics builder. +type MetricsBuilderConfig struct { + Metrics MetricsConfig `mapstructure:"metrics"` + ResourceAttributes ResourceAttributesConfig `mapstructure:"resource_attributes"` +} + +func DefaultMetricsBuilderConfig() MetricsBuilderConfig { + return MetricsBuilderConfig{ + Metrics: DefaultMetricsConfig(), + ResourceAttributes: DefaultResourceAttributesConfig(), + } +} diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_config_test.go b/receiver/tlscheckreceiver/internal/metadata/generated_config_test.go new file mode 100644 index 000000000000..7c84015ab981 --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_config_test.go @@ -0,0 +1,109 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/confmap/confmaptest" +) + +func TestMetricsBuilderConfig(t *testing.T) { + tests := []struct { + name string + want MetricsBuilderConfig + }{ + { + name: "default", + want: DefaultMetricsBuilderConfig(), + }, + { + name: "all_set", + want: MetricsBuilderConfig{ + Metrics: MetricsConfig{ + TlscheckTimeLeft: MetricConfig{Enabled: true}, + }, + ResourceAttributes: ResourceAttributesConfig{ + TlscheckURL: ResourceAttributeConfig{Enabled: true}, + }, + }, + }, + { + name: "none_set", + want: MetricsBuilderConfig{ + Metrics: MetricsConfig{ + TlscheckTimeLeft: MetricConfig{Enabled: false}, + }, + ResourceAttributes: ResourceAttributesConfig{ + TlscheckURL: ResourceAttributeConfig{Enabled: false}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadMetricsBuilderConfig(t, tt.name) + if diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(MetricConfig{}, ResourceAttributeConfig{})); diff != "" { + t.Errorf("Config mismatch (-expected +actual):\n%s", diff) + } + }) + } +} + +func loadMetricsBuilderConfig(t *testing.T, name string) MetricsBuilderConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + cfg := DefaultMetricsBuilderConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} + +func TestResourceAttributesConfig(t *testing.T) { + tests := []struct { + name string + want ResourceAttributesConfig + }{ + { + name: "default", + want: DefaultResourceAttributesConfig(), + }, + { + name: "all_set", + want: ResourceAttributesConfig{ + TlscheckURL: ResourceAttributeConfig{Enabled: true}, + }, + }, + { + name: "none_set", + want: ResourceAttributesConfig{ + TlscheckURL: ResourceAttributeConfig{Enabled: false}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadResourceAttributesConfig(t, tt.name) + if diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(ResourceAttributeConfig{})); diff != "" { + t.Errorf("Config mismatch (-expected +actual):\n%s", diff) + } + }) + } +} + +func loadResourceAttributesConfig(t *testing.T, name string) ResourceAttributesConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + sub, err = sub.Sub("resource_attributes") + require.NoError(t, err) + cfg := DefaultResourceAttributesConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_metrics.go b/receiver/tlscheckreceiver/internal/metadata/generated_metrics.go new file mode 100644 index 000000000000..988cf4f1cdcb --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_metrics.go @@ -0,0 +1,227 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/filter" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver" +) + +type metricTlscheckTimeLeft struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills tlscheck.time_left metric with initial data. +func (m *metricTlscheckTimeLeft) init() { + m.data.SetName("tlscheck.time_left") + m.data.SetDescription("Time in seconds until certificate expiry, as specified by `NotAfter` field in the x.509 certificate. Negative values represent time in seconds since expiration.") + m.data.SetUnit("s") + m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricTlscheckTimeLeft) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, tlscheckX509IssuerAttributeValue string, tlscheckX509CnAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("tlscheck.x509.issuer", tlscheckX509IssuerAttributeValue) + dp.Attributes().PutStr("tlscheck.x509.cn", tlscheckX509CnAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricTlscheckTimeLeft) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricTlscheckTimeLeft) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricTlscheckTimeLeft(cfg MetricConfig) metricTlscheckTimeLeft { + m := metricTlscheckTimeLeft{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +// MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations +// required to produce metric representation defined in metadata and user config. +type MetricsBuilder struct { + config MetricsBuilderConfig // config of the metrics builder. + startTime pcommon.Timestamp // start time that will be applied to all recorded data points. + metricsCapacity int // maximum observed number of metrics per resource. + metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. + buildInfo component.BuildInfo // contains version information. + resourceAttributeIncludeFilter map[string]filter.Filter + resourceAttributeExcludeFilter map[string]filter.Filter + metricTlscheckTimeLeft metricTlscheckTimeLeft +} + +// MetricBuilderOption applies changes to default metrics builder. +type MetricBuilderOption interface { + apply(*MetricsBuilder) +} + +type metricBuilderOptionFunc func(mb *MetricsBuilder) + +func (mbof metricBuilderOptionFunc) apply(mb *MetricsBuilder) { + mbof(mb) +} + +// WithStartTime sets startTime on the metrics builder. +func WithStartTime(startTime pcommon.Timestamp) MetricBuilderOption { + return metricBuilderOptionFunc(func(mb *MetricsBuilder) { + mb.startTime = startTime + }) +} + +func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, options ...MetricBuilderOption) *MetricsBuilder { + mb := &MetricsBuilder{ + config: mbc, + startTime: pcommon.NewTimestampFromTime(time.Now()), + metricsBuffer: pmetric.NewMetrics(), + buildInfo: settings.BuildInfo, + metricTlscheckTimeLeft: newMetricTlscheckTimeLeft(mbc.Metrics.TlscheckTimeLeft), + resourceAttributeIncludeFilter: make(map[string]filter.Filter), + resourceAttributeExcludeFilter: make(map[string]filter.Filter), + } + if mbc.ResourceAttributes.TlscheckURL.MetricsInclude != nil { + mb.resourceAttributeIncludeFilter["tlscheck.url"] = filter.CreateFilter(mbc.ResourceAttributes.TlscheckURL.MetricsInclude) + } + if mbc.ResourceAttributes.TlscheckURL.MetricsExclude != nil { + mb.resourceAttributeExcludeFilter["tlscheck.url"] = filter.CreateFilter(mbc.ResourceAttributes.TlscheckURL.MetricsExclude) + } + + for _, op := range options { + op.apply(mb) + } + return mb +} + +// NewResourceBuilder returns a new resource builder that should be used to build a resource associated with for the emitted metrics. +func (mb *MetricsBuilder) NewResourceBuilder() *ResourceBuilder { + return NewResourceBuilder(mb.config.ResourceAttributes) +} + +// updateCapacity updates max length of metrics and resource attributes that will be used for the slice capacity. +func (mb *MetricsBuilder) updateCapacity(rm pmetric.ResourceMetrics) { + if mb.metricsCapacity < rm.ScopeMetrics().At(0).Metrics().Len() { + mb.metricsCapacity = rm.ScopeMetrics().At(0).Metrics().Len() + } +} + +// ResourceMetricsOption applies changes to provided resource metrics. +type ResourceMetricsOption interface { + apply(pmetric.ResourceMetrics) +} + +type resourceMetricsOptionFunc func(pmetric.ResourceMetrics) + +func (rmof resourceMetricsOptionFunc) apply(rm pmetric.ResourceMetrics) { + rmof(rm) +} + +// WithResource sets the provided resource on the emitted ResourceMetrics. +// It's recommended to use ResourceBuilder to create the resource. +func WithResource(res pcommon.Resource) ResourceMetricsOption { + return resourceMetricsOptionFunc(func(rm pmetric.ResourceMetrics) { + res.CopyTo(rm.Resource()) + }) +} + +// WithStartTimeOverride overrides start time for all the resource metrics data points. +// This option should be only used if different start time has to be set on metrics coming from different resources. +func WithStartTimeOverride(start pcommon.Timestamp) ResourceMetricsOption { + return resourceMetricsOptionFunc(func(rm pmetric.ResourceMetrics) { + var dps pmetric.NumberDataPointSlice + metrics := rm.ScopeMetrics().At(0).Metrics() + for i := 0; i < metrics.Len(); i++ { + switch metrics.At(i).Type() { + case pmetric.MetricTypeGauge: + dps = metrics.At(i).Gauge().DataPoints() + case pmetric.MetricTypeSum: + dps = metrics.At(i).Sum().DataPoints() + } + for j := 0; j < dps.Len(); j++ { + dps.At(j).SetStartTimestamp(start) + } + } + }) +} + +// EmitForResource saves all the generated metrics under a new resource and updates the internal state to be ready for +// recording another set of data points as part of another resource. This function can be helpful when one scraper +// needs to emit metrics from several resources. Otherwise calling this function is not required, +// just `Emit` function can be called instead. +// Resource attributes should be provided as ResourceMetricsOption arguments. +func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { + rm := pmetric.NewResourceMetrics() + ils := rm.ScopeMetrics().AppendEmpty() + ils.Scope().SetName("github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver") + ils.Scope().SetVersion(mb.buildInfo.Version) + ils.Metrics().EnsureCapacity(mb.metricsCapacity) + mb.metricTlscheckTimeLeft.emit(ils.Metrics()) + + for _, op := range options { + op.apply(rm) + } + for attr, filter := range mb.resourceAttributeIncludeFilter { + if val, ok := rm.Resource().Attributes().Get(attr); ok && !filter.Matches(val.AsString()) { + return + } + } + for attr, filter := range mb.resourceAttributeExcludeFilter { + if val, ok := rm.Resource().Attributes().Get(attr); ok && filter.Matches(val.AsString()) { + return + } + } + + if ils.Metrics().Len() > 0 { + mb.updateCapacity(rm) + rm.MoveTo(mb.metricsBuffer.ResourceMetrics().AppendEmpty()) + } +} + +// Emit returns all the metrics accumulated by the metrics builder and updates the internal state to be ready for +// recording another set of metrics. This function will be responsible for applying all the transformations required to +// produce metric representation defined in metadata and user config, e.g. delta or cumulative. +func (mb *MetricsBuilder) Emit(options ...ResourceMetricsOption) pmetric.Metrics { + mb.EmitForResource(options...) + metrics := mb.metricsBuffer + mb.metricsBuffer = pmetric.NewMetrics() + return metrics +} + +// RecordTlscheckTimeLeftDataPoint adds a data point to tlscheck.time_left metric. +func (mb *MetricsBuilder) RecordTlscheckTimeLeftDataPoint(ts pcommon.Timestamp, val int64, tlscheckX509IssuerAttributeValue string, tlscheckX509CnAttributeValue string) { + mb.metricTlscheckTimeLeft.recordDataPoint(mb.startTime, ts, val, tlscheckX509IssuerAttributeValue, tlscheckX509CnAttributeValue) +} + +// Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, +// and metrics builder should update its startTime and reset it's internal state accordingly. +func (mb *MetricsBuilder) Reset(options ...MetricBuilderOption) { + mb.startTime = pcommon.NewTimestampFromTime(time.Now()) + for _, op := range options { + op.apply(mb) + } +} diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_metrics_test.go b/receiver/tlscheckreceiver/internal/metadata/generated_metrics_test.go new file mode 100644 index 000000000000..b81713f24b82 --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_metrics_test.go @@ -0,0 +1,121 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver/receivertest" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +type testDataSet int + +const ( + testDataSetDefault testDataSet = iota + testDataSetAll + testDataSetNone +) + +func TestMetricsBuilder(t *testing.T) { + tests := []struct { + name string + metricsSet testDataSet + resAttrsSet testDataSet + expectEmpty bool + }{ + { + name: "default", + }, + { + name: "all_set", + metricsSet: testDataSetAll, + resAttrsSet: testDataSetAll, + }, + { + name: "none_set", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "filter_set_include", + resAttrsSet: testDataSetAll, + }, + { + name: "filter_set_exclude", + resAttrsSet: testDataSetAll, + expectEmpty: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + start := pcommon.Timestamp(1_000_000_000) + ts := pcommon.Timestamp(1_000_001_000) + observedZapCore, observedLogs := observer.New(zap.WarnLevel) + settings := receivertest.NewNopSettings() + settings.Logger = zap.New(observedZapCore) + mb := NewMetricsBuilder(loadMetricsBuilderConfig(t, tt.name), settings, WithStartTime(start)) + + expectedWarnings := 0 + + assert.Equal(t, expectedWarnings, observedLogs.Len()) + + defaultMetricsCount := 0 + allMetricsCount := 0 + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordTlscheckTimeLeftDataPoint(ts, 1, "tlscheck.x509.issuer-val", "tlscheck.x509.cn-val") + + rb := mb.NewResourceBuilder() + rb.SetTlscheckURL("tlscheck.url-val") + res := rb.Emit() + metrics := mb.Emit(WithResource(res)) + + if tt.expectEmpty { + assert.Equal(t, 0, metrics.ResourceMetrics().Len()) + return + } + + assert.Equal(t, 1, metrics.ResourceMetrics().Len()) + rm := metrics.ResourceMetrics().At(0) + assert.Equal(t, res, rm.Resource()) + assert.Equal(t, 1, rm.ScopeMetrics().Len()) + ms := rm.ScopeMetrics().At(0).Metrics() + if tt.metricsSet == testDataSetDefault { + assert.Equal(t, defaultMetricsCount, ms.Len()) + } + if tt.metricsSet == testDataSetAll { + assert.Equal(t, allMetricsCount, ms.Len()) + } + validatedMetrics := make(map[string]bool) + for i := 0; i < ms.Len(); i++ { + switch ms.At(i).Name() { + case "tlscheck.time_left": + assert.False(t, validatedMetrics["tlscheck.time_left"], "Found a duplicate in the metrics slice: tlscheck.time_left") + validatedMetrics["tlscheck.time_left"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Time in seconds until certificate expiry, as specified by `NotAfter` field in the x.509 certificate. Negative values represent time in seconds since expiration.", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("tlscheck.x509.issuer") + assert.True(t, ok) + assert.EqualValues(t, "tlscheck.x509.issuer-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("tlscheck.x509.cn") + assert.True(t, ok) + assert.EqualValues(t, "tlscheck.x509.cn-val", attrVal.Str()) + } + } + }) + } +} diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_resource.go b/receiver/tlscheckreceiver/internal/metadata/generated_resource.go new file mode 100644 index 000000000000..e51961b1db39 --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_resource.go @@ -0,0 +1,36 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/pdata/pcommon" +) + +// ResourceBuilder is a helper struct to build resources predefined in metadata.yaml. +// The ResourceBuilder is not thread-safe and must not to be used in multiple goroutines. +type ResourceBuilder struct { + config ResourceAttributesConfig + res pcommon.Resource +} + +// NewResourceBuilder creates a new ResourceBuilder. This method should be called on the start of the application. +func NewResourceBuilder(rac ResourceAttributesConfig) *ResourceBuilder { + return &ResourceBuilder{ + config: rac, + res: pcommon.NewResource(), + } +} + +// SetTlscheckURL sets provided value as "tlscheck.url" attribute. +func (rb *ResourceBuilder) SetTlscheckURL(val string) { + if rb.config.TlscheckURL.Enabled { + rb.res.Attributes().PutStr("tlscheck.url", val) + } +} + +// Emit returns the built resource and resets the internal builder state. +func (rb *ResourceBuilder) Emit() pcommon.Resource { + r := rb.res + rb.res = pcommon.NewResource() + return r +} diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_resource_test.go b/receiver/tlscheckreceiver/internal/metadata/generated_resource_test.go new file mode 100644 index 000000000000..4a67d0fd5ad5 --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_resource_test.go @@ -0,0 +1,40 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResourceBuilder(t *testing.T) { + for _, tt := range []string{"default", "all_set", "none_set"} { + t.Run(tt, func(t *testing.T) { + cfg := loadResourceAttributesConfig(t, tt) + rb := NewResourceBuilder(cfg) + rb.SetTlscheckURL("tlscheck.url-val") + + res := rb.Emit() + assert.Equal(t, 0, rb.Emit().Attributes().Len()) // Second call should return empty Resource + + switch tt { + case "default": + assert.Equal(t, 1, res.Attributes().Len()) + case "all_set": + assert.Equal(t, 1, res.Attributes().Len()) + case "none_set": + assert.Equal(t, 0, res.Attributes().Len()) + return + default: + assert.Failf(t, "unexpected test case: %s", tt) + } + + val, ok := res.Attributes().Get("tlscheck.url") + assert.True(t, ok) + if ok { + assert.EqualValues(t, "tlscheck.url-val", val.Str()) + } + }) + } +} diff --git a/receiver/tlscheckreceiver/internal/metadata/generated_status.go b/receiver/tlscheckreceiver/internal/metadata/generated_status.go new file mode 100644 index 000000000000..5a9e7e63556d --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/generated_status.go @@ -0,0 +1,16 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("tlscheck") + ScopeName = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" +) + +const ( + MetricsStability = component.StabilityLevelDevelopment +) diff --git a/receiver/tlscheckreceiver/internal/metadata/testdata/config.yaml b/receiver/tlscheckreceiver/internal/metadata/testdata/config.yaml new file mode 100644 index 000000000000..7dc13e51f71c --- /dev/null +++ b/receiver/tlscheckreceiver/internal/metadata/testdata/config.yaml @@ -0,0 +1,27 @@ +default: +all_set: + metrics: + tlscheck.time_left: + enabled: true + resource_attributes: + tlscheck.url: + enabled: true +none_set: + metrics: + tlscheck.time_left: + enabled: false + resource_attributes: + tlscheck.url: + enabled: false +filter_set_include: + resource_attributes: + tlscheck.url: + enabled: true + metrics_include: + - regexp: ".*" +filter_set_exclude: + resource_attributes: + tlscheck.url: + enabled: true + metrics_exclude: + - strict: "tlscheck.url-val" diff --git a/receiver/tlscheckreceiver/metadata.yaml b/receiver/tlscheckreceiver/metadata.yaml new file mode 100644 index 000000000000..843444b4c35f --- /dev/null +++ b/receiver/tlscheckreceiver/metadata.yaml @@ -0,0 +1,35 @@ +type: tlscheck + +status: + class: receiver + stability: + development: [metrics] + distributions: [contrib] + codeowners: + active: [atoulme, michael-burt] + + +resource_attributes: + tlscheck.url: + enabled: true + description: Url at which the certificate was accessed. + type: string + +attributes: + tlscheck.x509.issuer: + enabled: true + description: The entity that issued the certificate. + type: string + tlscheck.x509.cn: + enabled: true + description: The commonName in the subject of the certificate. + type: string + +metrics: + tlscheck.time_left: + description: Time in seconds until certificate expiry, as specified by `NotAfter` field in the x.509 certificate. Negative values represent time in seconds since expiration. + enabled: true + gauge: + value_type: int + unit: "s" + attributes: [tlscheck.x509.issuer, tlscheck.x509.cn] \ No newline at end of file diff --git a/receiver/tlscheckreceiver/scraper.go b/receiver/tlscheckreceiver/scraper.go new file mode 100644 index 000000000000..c4807cc78eff --- /dev/null +++ b/receiver/tlscheckreceiver/scraper.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver" + +import ( + "context" + + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver/internal/metadata" +) + +type scraper struct { + // include string + logger *zap.Logger + mb *metadata.MetricsBuilder +} + +func (s *scraper) scrape(_ context.Context) (pmetric.Metrics, error) { + return pmetric.NewMetrics(), nil +} + +func newScraper(cfg *Config, settings receiver.Settings) *scraper { + return &scraper{ + logger: settings.TelemetrySettings.Logger, + mb: metadata.NewMetricsBuilder(cfg.MetricsBuilderConfig, settings), + } +} diff --git a/reports/distributions/contrib.yaml b/reports/distributions/contrib.yaml index a915a45f43fa..32d24599ba99 100644 --- a/reports/distributions/contrib.yaml +++ b/reports/distributions/contrib.yaml @@ -179,6 +179,7 @@ components: - statsd - syslog - tcplog + - tlscheck - udplog - vcenter - wavefront diff --git a/versions.yaml b/versions.yaml index 974edb7119d5..e6fee73f2ea2 100644 --- a/versions.yaml +++ b/versions.yaml @@ -274,6 +274,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/syslogreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tcplogreceiver + - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/udplogreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/vcenterreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/wavefrontreceiver