diff --git a/CHANGELOG.md b/CHANGELOG.md index f3fd4c1f6f..9b6741bf71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - (Splunk) Update splunk-otel-js to [v2.6.0](https://github.com/signalfx/splunk-otel-js/releases/tag/v2.6.0) ([#4064](https://github.com/signalfx/splunk-otel-collector/pull/4064)) +### 🛑 Breaking changes 🛑 + +- `postgresql` discovery now uses the OpenTelemetry Collector Contrib receiver by default instead of the smartagent receiver. + ## v0.91.0 This Splunk OpenTelemetry Collector release includes changes from the [opentelemetry-collector v0.91.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.91.0) and the [opentelemetry-collector-contrib v0.91.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.91.0) releases where appropriate. diff --git a/cmd/otelcol/config/collector/config.d.linux/properties.discovery.yaml.example b/cmd/otelcol/config/collector/config.d.linux/properties.discovery.yaml.example index 279c8a8595..996df50aa7 100644 --- a/cmd/otelcol/config/collector/config.d.linux/properties.discovery.yaml.example +++ b/cmd/otelcol/config/collector/config.d.linux/properties.discovery.yaml.example @@ -11,9 +11,11 @@ splunk.discovery: k8s_observer: enabled: true receivers: + postgresql: + enabled: true smartagent/collectd/mysql: enabled: true smartagent/collectd/nginx: enabled: true smartagent/postgresql: - enabled: true + enabled: false diff --git a/cmd/otelcol/config/collector/config.d.linux/receivers/postgresql.discovery.yaml b/cmd/otelcol/config/collector/config.d.linux/receivers/postgresql.discovery.yaml new file mode 100644 index 0000000000..c3e6559338 --- /dev/null +++ b/cmd/otelcol/config/collector/config.d.linux/receivers/postgresql.discovery.yaml @@ -0,0 +1,83 @@ +##################################################################################### +# This file is generated by the Splunk Distribution of the OpenTelemetry Collector. # +# # +# It reflects the default configuration bundled in the Collector executable for use # +# in discovery mode (--discovery) and is provided for reference or customization. # +# Please note that any changes made to this file will need to be reconciled during # +# upgrades of the Collector. # +##################################################################################### +# postgresql: +# enabled: true +# rule: +# docker_observer: type == "container" and any([name, image, command], {# matches "(?i)postgres"}) and not (command matches "splunk.discovery") +# host_observer: type == "hostport" and command matches "(?i)postgres" and not (command matches "splunk.discovery") +# k8s_observer: type == "port" and pod.name matches "(?i)postgres" +# config: +# default: +# username: splunk.discovery.default +# password: splunk.discovery.default +# status: +# metrics: +# successful: +# - strict: postgresql.commits +# first_only: true +# log_record: +# severity_text: info +# body: PostgreSQL receiver is working! +# statements: +# failed: +# - regexp: 'connect: network is unreachable' +# first_only: true +# log_record: +# severity_text: info +# append_pattern: true +# body: The container cannot be reached by the Collector. Make sure they're in the same network. +# - regexp: 'connect: connection refused' +# first_only: true +# log_record: +# severity_text: info +# append_pattern: true +# body: The container is refusing PostgreSQL connections. +# partial: +# - regexp: 'pq: password authentication failed for user' +# first_only: true +# log_record: +# severity_text: info +# append_pattern: true +# body: >- +# Please ensure your user credentials are correctly specified with +# `--set splunk.discovery.receivers.postgresql.config.username=""` and +# `--set splunk.discovery.receivers.postgresql.config.password=""` or +# `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_username=""` and +# `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_password=""` environment variables. +# - regexp: 'pq: database .* does not exist' +# first_only: true +# log_record: +# severity_text: info +# append_pattern: true +# body: >- +# Make sure the target database is correctly specified using the +# `--set splunk.discovery.receivers.postgresql.config.databases="[]"` command or the +# `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_databases="[]"` environment variable. +# - regexp: 'pq: SSL is not enabled on the server' +# first_only: true +# log_record: +# severity_text: info +# append_pattern: true +# body: >- +# Make sure the target database has SSL enabled or set insecure using the +# `--set splunk.discovery.receivers.postgresql.config.tls::insecure=""` command or the +# `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_tls_x3a__x3a_insecure=""` environment variable. +# - regexp: 'pq: pg_stat_statements must be loaded via shared_preload_libraries' +# first_only: true +# log_record: +# severity_text: info +# append_pattern: true +# body: >- +# Make sure your PostgreSQL database has +# `shared_preload_libraries = 'pg_stat_statements'` +# in the postgresql.conf file and that +# `CREATE EXTENSION IF NOT EXISTS pg_stat_statements;` +# has been run for each database you would like to monitor. +# For example: +# `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` diff --git a/cmd/otelcol/config/collector/config.d.linux/receivers/smartagent-postgresql.discovery.yaml b/cmd/otelcol/config/collector/config.d.linux/receivers/smartagent-postgresql.discovery.yaml index 86f4cf33ee..40ee4a400c 100644 --- a/cmd/otelcol/config/collector/config.d.linux/receivers/smartagent-postgresql.discovery.yaml +++ b/cmd/otelcol/config/collector/config.d.linux/receivers/smartagent-postgresql.discovery.yaml @@ -7,7 +7,7 @@ # upgrades of the Collector. # ##################################################################################### # smartagent/postgresql: -# enabled: true +# enabled: false # rule: # docker_observer: type == "container" and any([name, image, command], {# matches "(?i)postgres"}) and not (command matches "splunk.discovery") # host_observer: type == "hostport" and command matches "(?i)postgres" and not (command matches "splunk.discovery") @@ -86,4 +86,4 @@ # `CREATE EXTENSION IF NOT EXISTS pg_stat_statements;` # has been run for each database you would like to monitor. # For example: -# `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` +# `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` \ No newline at end of file diff --git a/internal/confmapprovider/discovery/README.md b/internal/confmapprovider/discovery/README.md index 83aede17dd..e1d23a7ef3 100644 --- a/internal/confmapprovider/discovery/README.md +++ b/internal/confmapprovider/discovery/README.md @@ -137,6 +137,7 @@ The following components have bundled discovery configurations in the last Splun I. Receivers * `oracledb` ([Linux and Windows](./bundle/bundle.d/receivers/oracledb.discovery.yaml)) +* `postgresql` ([Linux and Windows](./bundle/bundle.d/receivers/postgresql.discovery.yaml)) * `redis` ([Linux and Windows](./bundle/bundle.d/receivers/redis.discovery.yaml)) * `smartagent` with `collectd/mysql` monitor type ([Linux](./bundle/bundle.d/receivers/smartagent-collectd-mysql.discovery.yaml)) * `smartagent` with `collectd/nginx` monitor type ([Linux](./bundle/bundle.d/receivers/smartagent-collectd-nginx.discovery.yaml)) diff --git a/internal/confmapprovider/discovery/bundle/bundle.d/receivers/postgresql.discovery.yaml b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/postgresql.discovery.yaml new file mode 100644 index 0000000000..cc4446be29 --- /dev/null +++ b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/postgresql.discovery.yaml @@ -0,0 +1,79 @@ +##################################################################################### +# Do not edit manually! # +# All changes must be made to associated .tmpl file before running 'make bundle.d'. # +##################################################################################### +postgresql: + enabled: true + rule: + docker_observer: type == "container" and any([name, image, command], {# matches "(?i)postgres"}) and not (command matches "splunk.discovery") + host_observer: type == "hostport" and command matches "(?i)postgres" and not (command matches "splunk.discovery") + k8s_observer: type == "port" and pod.name matches "(?i)postgres" + config: + default: + username: splunk.discovery.default + password: splunk.discovery.default + status: + metrics: + successful: + - strict: postgresql.commits + first_only: true + log_record: + severity_text: info + body: PostgreSQL receiver is working! + statements: + failed: + - regexp: 'connect: network is unreachable' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: The container cannot be reached by the Collector. Make sure they're in the same network. + - regexp: 'connect: connection refused' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: The container is refusing PostgreSQL connections. + partial: + - regexp: 'pq: password authentication failed for user' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Please ensure your user credentials are correctly specified with + `--set splunk.discovery.receivers.postgresql.config.username=""` and + `--set splunk.discovery.receivers.postgresql.config.password=""` or + `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_username=""` and + `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_password=""` environment variables. + - regexp: 'pq: database .* does not exist' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Make sure the target database is correctly specified using the + `--set splunk.discovery.receivers.postgresql.config.databases="[]"` command or the + `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_databases="[]"` environment variable. + - regexp: 'pq: SSL is not enabled on the server' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Make sure the target database has SSL enabled or set insecure using the + `--set splunk.discovery.receivers.postgresql.config.tls::insecure=""` command or the + `SPLUNK_DISCOVERY_RECEIVERS_postgresql_CONFIG_tls_x3a__x3a_insecure=""` environment variable. + - regexp: 'pq: pg_stat_statements must be loaded via shared_preload_libraries' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Make sure your PostgreSQL database has + `shared_preload_libraries = 'pg_stat_statements'` + in the postgresql.conf file and that + `CREATE EXTENSION IF NOT EXISTS pg_stat_statements;` + has been run for each database you would like to monitor. + For example: + `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` diff --git a/internal/confmapprovider/discovery/bundle/bundle.d/receivers/postgresql.discovery.yaml.tmpl b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/postgresql.discovery.yaml.tmpl new file mode 100644 index 0000000000..d2d2a6f583 --- /dev/null +++ b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/postgresql.discovery.yaml.tmpl @@ -0,0 +1,75 @@ +{{ receiver "postgresql" }}: + enabled: true + rule: + docker_observer: type == "container" and any([name, image, command], {# matches "(?i)postgres"}) and not (command matches "splunk.discovery") + host_observer: type == "hostport" and command matches "(?i)postgres" and not (command matches "splunk.discovery") + k8s_observer: type == "port" and pod.name matches "(?i)postgres" + config: + default: + username: {{ defaultValue }} + password: {{ defaultValue }} + status: + metrics: + successful: + - strict: postgresql.commits + first_only: true + log_record: + severity_text: info + body: PostgreSQL receiver is working! + statements: + failed: + - regexp: 'connect: network is unreachable' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: The container cannot be reached by the Collector. Make sure they're in the same network. + - regexp: 'connect: connection refused' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: The container is refusing PostgreSQL connections. + partial: + - regexp: 'pq: password authentication failed for user' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Please ensure your user credentials are correctly specified with + `--set {{ configProperty "username" "" }}` and + `--set {{ configProperty "password" "" }}` or + `{{ configPropertyEnvVar "username" "" }}` and + `{{ configPropertyEnvVar "password" "" }}` environment variables. + - regexp: 'pq: database .* does not exist' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Make sure the target database is correctly specified using the + `--set {{ configProperty "databases" "[]" }}` command or the + `{{ configPropertyEnvVar "databases" "[]" }}` environment variable. + - regexp: 'pq: SSL is not enabled on the server' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Make sure the target database has SSL enabled or set insecure using the + `--set {{ configProperty "tls::insecure" "" }}` command or the + `{{ configPropertyEnvVar "tls::insecure" "" }}` environment variable. + - regexp: 'pq: pg_stat_statements must be loaded via shared_preload_libraries' + first_only: true + log_record: + severity_text: info + append_pattern: true + body: >- + Make sure your PostgreSQL database has + `shared_preload_libraries = 'pg_stat_statements'` + in the postgresql.conf file and that + `CREATE EXTENSION IF NOT EXISTS pg_stat_statements;` + has been run for each database you would like to monitor. + For example: + `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` diff --git a/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml index 6c911fe71a..117044e6ee 100644 --- a/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml +++ b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml @@ -3,7 +3,7 @@ # All changes must be made to associated .tmpl file before running 'make bundle.d'. # ##################################################################################### smartagent/postgresql: - enabled: true + enabled: false rule: docker_observer: type == "container" and any([name, image, command], {# matches "(?i)postgres"}) and not (command matches "splunk.discovery") host_observer: type == "hostport" and command matches "(?i)postgres" and not (command matches "splunk.discovery") @@ -82,4 +82,4 @@ smartagent/postgresql: `CREATE EXTENSION IF NOT EXISTS pg_stat_statements;` has been run for each database you would like to monitor. For example: - `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` + `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` \ No newline at end of file diff --git a/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml.tmpl b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml.tmpl index 4a243f6449..6fab6001ca 100644 --- a/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml.tmpl +++ b/internal/confmapprovider/discovery/bundle/bundle.d/receivers/smartagent-postgresql.discovery.yaml.tmpl @@ -1,5 +1,5 @@ {{ receiver "smartagent/postgresql" }}: - enabled: true + enabled: false rule: docker_observer: type == "container" and any([name, image, command], {# matches "(?i)postgres"}) and not (command matches "splunk.discovery") host_observer: type == "hostport" and command matches "(?i)postgres" and not (command matches "splunk.discovery") @@ -78,4 +78,4 @@ `CREATE EXTENSION IF NOT EXISTS pg_stat_statements;` has been run for each database you would like to monitor. For example: - `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` + `psql --dbname "" -c "CREATE EXTENSION pg_stat_statements;"` \ No newline at end of file diff --git a/internal/confmapprovider/discovery/bundle/bundle_gen.go b/internal/confmapprovider/discovery/bundle/bundle_gen.go index 86870329cb..68f1c86424 100644 --- a/internal/confmapprovider/discovery/bundle/bundle_gen.go +++ b/internal/confmapprovider/discovery/bundle/bundle_gen.go @@ -25,6 +25,8 @@ //go:generate discoverybundler -r -t bundle.d/receivers/oracledb.discovery.yaml.tmpl //go:generate discoverybundler -r -c -d ../../../../cmd/otelcol/config/collector/config.d.linux/receivers -t bundle.d/receivers/oracledb.discovery.yaml.tmpl +//go:generate discoverybundler -r -t bundle.d/receivers/postgresql.discovery.yaml.tmpl +//go:generate discoverybundler -r -c -d ../../../../cmd/otelcol/config/collector/config.d.linux/receivers -t bundle.d/receivers/postgresql.discovery.yaml.tmpl //go:generate discoverybundler -r -t bundle.d/receivers/redis.discovery.yaml.tmpl //go:generate discoverybundler -r -c -d ../../../../cmd/otelcol/config/collector/config.d.linux/receivers -t bundle.d/receivers/redis.discovery.yaml.tmpl //go:generate discoverybundler -r -t bundle.d/receivers/smartagent-collectd-mysql.discovery.yaml.tmpl diff --git a/internal/confmapprovider/discovery/bundle/bundledfs_other_test.go b/internal/confmapprovider/discovery/bundle/bundledfs_other_test.go index 64f14c0baf..2230c9756c 100644 --- a/internal/confmapprovider/discovery/bundle/bundledfs_other_test.go +++ b/internal/confmapprovider/discovery/bundle/bundledfs_other_test.go @@ -28,6 +28,7 @@ func TestBundleDir(t *testing.T) { require.NoError(t, err) require.Equal(t, []string{ "bundle.d/receivers/oracledb.discovery.yaml", + "bundle.d/receivers/postgresql.discovery.yaml", "bundle.d/receivers/redis.discovery.yaml", "bundle.d/receivers/smartagent-collectd-mysql.discovery.yaml", "bundle.d/receivers/smartagent-collectd-nginx.discovery.yaml", diff --git a/internal/confmapprovider/discovery/bundle/bundledfs_others.go b/internal/confmapprovider/discovery/bundle/bundledfs_others.go index cfbd2de7e5..b46c7ca5ce 100644 --- a/internal/confmapprovider/discovery/bundle/bundledfs_others.go +++ b/internal/confmapprovider/discovery/bundle/bundledfs_others.go @@ -26,6 +26,7 @@ import ( //go:embed bundle.d/extensions/host-observer.discovery.yaml //go:embed bundle.d/extensions/k8s-observer.discovery.yaml //go:embed bundle.d/receivers/oracledb.discovery.yaml +//go:embed bundle.d/receivers/postgresql.discovery.yaml //go:embed bundle.d/receivers/redis.discovery.yaml //go:embed bundle.d/receivers/smartagent-collectd-mysql.discovery.yaml //go:embed bundle.d/receivers/smartagent-collectd-nginx.discovery.yaml diff --git a/internal/confmapprovider/discovery/bundle/bundledfs_windows.go b/internal/confmapprovider/discovery/bundle/bundledfs_windows.go index cdffa4c274..45c49f9cda 100644 --- a/internal/confmapprovider/discovery/bundle/bundledfs_windows.go +++ b/internal/confmapprovider/discovery/bundle/bundledfs_windows.go @@ -26,6 +26,7 @@ import ( //go:embed bundle.d/extensions/host-observer.discovery.yaml //go:embed bundle.d/extensions/k8s-observer.discovery.yaml //go:embed bundle.d/receivers/oracledb.discovery.yaml +//go:embed bundle.d/receivers/postgresql.discovery.yaml //go:embed bundle.d/receivers/redis.discovery.yaml //go:embed bundle.d/receivers/smartagent-postgresql.discovery.yaml var BundledFS embed.FS diff --git a/internal/confmapprovider/discovery/bundle/bundledfs_windows_test.go b/internal/confmapprovider/discovery/bundle/bundledfs_windows_test.go index 8eb5d07084..1f77367baa 100644 --- a/internal/confmapprovider/discovery/bundle/bundledfs_windows_test.go +++ b/internal/confmapprovider/discovery/bundle/bundledfs_windows_test.go @@ -28,6 +28,7 @@ func TestBundleDir(t *testing.T) { require.NoError(t, err) require.Equal(t, []string{ "bundle.d/receivers/oracledb.discovery.yaml", + "bundle.d/receivers/postgresql.discovery.yaml", "bundle.d/receivers/redis.discovery.yaml", "bundle.d/receivers/smartagent-postgresql.discovery.yaml", }, receivers) diff --git a/internal/confmapprovider/discovery/bundle/components.go b/internal/confmapprovider/discovery/bundle/components.go index c1a927d252..b936bb46b2 100644 --- a/internal/confmapprovider/discovery/bundle/components.go +++ b/internal/confmapprovider/discovery/bundle/components.go @@ -32,6 +32,7 @@ var ( // in Components.Linux. If desired in windows BundledFS, ensure they are included in Components.Windows. receivers = []string{ "oracledb", + "postgresql", "redis", "smartagent-collectd-mysql", "smartagent-collectd-nginx", @@ -60,6 +61,7 @@ var ( Windows: func() map[string]struct{} { windows := map[string]struct{}{ "oracledb": {}, + "postgresql": {}, "redis": {}, "smartagent-postgresql": {}, } diff --git a/tests/receivers/postgresql/bundled_test.go b/tests/receivers/postgresql/bundled_test.go new file mode 100644 index 0000000000..ae0a723295 --- /dev/null +++ b/tests/receivers/postgresql/bundled_test.go @@ -0,0 +1,412 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package tests + +import ( + "context" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "gopkg.in/yaml.v3" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/signalfx/splunk-otel-collector/tests/testutils" + "github.com/signalfx/splunk-otel-collector/tests/testutils/kubeutils" + "github.com/signalfx/splunk-otel-collector/tests/testutils/kubeutils/manifests" +) + +func TestPostgresqlDockerObserver(t *testing.T) { + testutils.SkipIfNotContainerTest(t) + if runtime.GOOS == "darwin" { + t.Skip("unable to share sockets between mac and d4m vm: https://github.com/docker/for-mac/issues/483#issuecomment-758836836") + } + + testutils.AssertAllMetricsReceived(t, "bundled.yaml", "otlp_exporter.yaml", + postgresqldb, []testutils.CollectorBuilder{ + func(c testutils.Collector) testutils.Collector { + cc := c.(*testutils.CollectorContainer) + cc.Container = cc.Container.WithBinds("/var/run/docker.sock:/var/run/docker.sock:ro") + cc.Container = cc.Container.WillWaitForLogs("Discovering for next") + cc.Container = cc.Container.WithUser(fmt.Sprintf("999:%d", testutils.GetDockerGID(t))) + return cc + }, + func(collector testutils.Collector) testutils.Collector { + return collector.WithEnv(map[string]string{ + "SPLUNK_DISCOVERY_DURATION": "10s", + // confirm that debug logging doesn't affect runtime + "SPLUNK_DISCOVERY_LOG_LEVEL": "debug", + }).WithArgs( + "--discovery", + "--set", "splunk.discovery.receivers.postgresql.config.username=otelu", + "--set", "splunk.discovery.receivers.postgresql.config.password=otelp", + "--set", "splunk.discovery.receivers.postgresql.config.tls::insecure=true", + //Disabling postgresql.backends metric that doesn't dependably show up in metrics during testing + "--set", "splunk.discovery.receivers.postgresql.config.metrics::postgresql.backends::enabled=false", + "--set", `splunk.discovery.extensions.k8s_observer.enabled=false`, + "--set", `splunk.discovery.extensions.host_observer.enabled=false`, + ) + }, + }, + ) +} + +func TestK8sObserver(t *testing.T) { + testutils.SkipIfNotContainerTest(t) + tc := testutils.NewTestcase(t, testutils.OTLPReceiverSinkAllInterfaces) + defer tc.PrintLogsOnFailure() + defer tc.ShutdownOTLPReceiverSink() + + cluster := testCluster{kubeutils.NewKindCluster(tc)} + defer cluster.Teardown() + cluster.Create() + cluster.LoadLocalCollectorImageIfNecessary() + + namespace := manifests.Namespace{Name: "test-namespace"} + serviceAccount := manifests.ServiceAccount{Name: "some.serviceacount", Namespace: namespace.Name} + clusterRole, clusterRoleBinding := clusterRoleAndBinding(namespace.Name, serviceAccount.Name) + sout, serr, err := cluster.Apply(manifests.RenderAll(t, namespace, serviceAccount, clusterRole, clusterRoleBinding)) + require.NoError(t, err, "stdout: %s, stderr: %s", sout, serr) + + postgresUID := cluster.createPostgres("target.postgres", namespace.Name, serviceAccount.Name) + + configMap := manifests.ConfigMap{ + Name: "collector.config", Namespace: namespace.Name, + Data: `config: | + exporters: + otlp: + endpoint: ${OTLP_ENDPOINT} + tls: + insecure: true + service: + pipelines: + metrics: + exporters: + - otlp +`} + + ds := cluster.daemonSet(namespace.Name, serviceAccount.Name, configMap.Name) + sout, serr, err = cluster.Apply(manifests.RenderAll(t, configMap, ds)) + require.NoError(t, err, "stdout: %s, stderr: %s", sout, serr) + + pods := cluster.WaitForPods(ds.Name, namespace.Name, 10*time.Minute) + require.Len(t, pods, 1) + collectorName := pods[0].Name + + expectedMetrics := tc.ResourceMetrics("all_k8s.yaml") + require.NoError(t, tc.OTLPReceiverSink.AssertAllMetricsReceived(t, *expectedMetrics, 30*time.Second)) + + stdOut, stdErr, err := cluster.Kubectl("logs", "-n", namespace.Name, collectorName) + require.NoError(t, err) + require.Contains( + t, stdOut.String(), + fmt.Sprintf(`Successfully discovered "postgresql" using "k8s_observer" endpoint "k8s_observer/%s/(5432)`, postgresUID), + stdErr.String(), + ) +} + +type testCluster struct{ *kubeutils.KindCluster } + +func (cluster testCluster) createPostgres(name, namespace, serviceAccount string) string { + dbsql, err := os.ReadFile(filepath.Join("..", "smartagent", "postgresql", "testdata", "server", "initdb.d", "db.sql")) + require.NoError(cluster.Testcase, err) + cmContent := map[string]any{"db.sql": string(dbsql)} + + initsh, err := os.ReadFile(filepath.Join("..", "smartagent", "postgresql", "testdata", "server", "initdb.d", "init.sh")) + require.NoError(cluster.Testcase, err) + cmContent["init.sh"] = string(initsh) + + requests, err := os.ReadFile(filepath.Join("..", "smartagent", "postgresql", "testdata", "client", "requests.sh")) + require.NoError(cluster.Testcase, err) + cmContent["requests.sh"] = string(requests) + + configMapContent, err := yaml.Marshal(cmContent) + require.NoError(cluster.Testcase, err) + + cm := manifests.ConfigMap{ + Namespace: namespace, + Name: "postgres", + Data: string(configMapContent), + } + sout, serr, err := cluster.Apply(cm.Render(cluster.Testcase)) + cluster.Testcase.Logger.Debug("applying ConfigMap", zap.String("stdout", sout.String()), zap.String("stderr", serr.String())) + require.NoError(cluster.Testcase, err) + + fileMode := int32(0777) + postgresID := int64(70) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + postgres, err := cluster.Clientset.CoreV1().Pods(namespace).Create( + ctx, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "postgres:13-alpine", + Name: "postgres-server", + Ports: []corev1.ContainerPort{{ContainerPort: 5432}}, + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &postgresID, + RunAsGroup: &postgresID, + }, + Command: []string{ + "docker-entrypoint.sh", + "-c", "shared_preload_libraries=pg_stat_statements", + "-c", "wal_level=logical", + "-c", "max_replication_slots=2", + }, + Env: []corev1.EnvVar{ + { + Name: "POSTGRES_DB", + Value: "test_db", + }, + { + Name: "POSTGRES_USER", + Value: "postgres", + }, + { + Name: "POSTGRES_PASSWORD", + Value: "postgres", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "initdb", MountPath: "/docker-entrypoint-initdb.d", + }, + }, + }, + { + Image: "postgres:13-alpine", + Name: "postgres-client", + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{"/opt/requests/requests.sh"}, + Env: []corev1.EnvVar{ + { + Name: "POSTGRES_SERVER", + Value: "localhost", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "requests", MountPath: "/opt/requests", + }, + }, + }, + }, + ServiceAccountName: serviceAccount, + HostNetwork: true, + Volumes: []corev1.Volume{ + { + Name: "initdb", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cm.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: "init.sh", + Path: "init.sh", + Mode: &fileMode, + }, + { + Key: "db.sql", + Path: "db.sql", + }, + }, + }, + }, + }, + { + Name: "requests", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cm.Name, + }, + Items: []corev1.KeyToPath{ + { + Key: "requests.sh", + Path: "requests.sh", + Mode: &fileMode, + }, + }, + }, + }, + }, + }, + }, + }, metav1.CreateOptions{}, + ) + require.NoError(cluster.Testcase, err) + + cluster.WaitForPods(postgres.Name, namespace, 10*time.Minute) + return string(postgres.UID) +} + +func clusterRoleAndBinding(namespace, serviceAccount string) (manifests.ClusterRole, manifests.ClusterRoleBinding) { + cr := manifests.ClusterRole{ + Name: "cluster-role", + Namespace: namespace, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{ + "events", + "namespaces", + "namespaces/status", + "nodes", + "nodes/spec", + "nodes/stats", + "nodes/proxy", + "pods", + "pods/status", + "persistentvolumeclaims", + "persistentvolumes", + "replicationcontrollers", + "replicationcontrollers/status", + "resourcequotas", + "services", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"apps"}, + Resources: []string{ + "daemonsets", + "deployments", + "replicasets", + "statefulsets", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"extensions"}, + Resources: []string{ + "daemonsets", + "deployments", + "replicasets", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"batch"}, + Resources: []string{ + "jobs", + "cronjobs", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"autoscaling"}, + Resources: []string{ + "horizontalpodautoscalers", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + NonResourceURLs: []string{"/metrics"}, + Verbs: []string{"get", "list", "watch"}, + }, + }, + } + + crb := manifests.ClusterRoleBinding{ + Namespace: namespace, + Name: "cluster-role-binding", + ClusterRoleName: cr.Name, + ServiceAccountName: serviceAccount, + } + return cr, crb +} + +func (cluster testCluster) daemonSet(namespace, serviceAccount, configMap string) manifests.DaemonSet { + splat := strings.Split(cluster.Testcase.OTLPEndpoint, ":") + port := splat[len(splat)-1] + var hostFromContainer string + if runtime.GOOS == "darwin" { + hostFromContainer = "host.docker.internal" + } else { + hostFromContainer = cluster.GetDefaultGatewayIP() + } + otlpEndpoint := fmt.Sprintf("%s:%s", hostFromContainer, port) + + return manifests.DaemonSet{ + Name: "an.agent.daemonset", + Namespace: namespace, + ServiceAccount: serviceAccount, + Labels: map[string]string{"label.key": "label.value"}, + Containers: []corev1.Container{ + { + Image: testutils.GetCollectorImageOrSkipTest(cluster.Testcase), + Command: []string{ + "/otelcol", "--config=/config/config.yaml", "--discovery", + "--set", "splunk.discovery.receivers.postgresql.config.username='${env:PG_USERNAME}'", + "--set", "splunk.discovery.receivers.postgresql.config.password='${env:PG_PASSWORD}'", + "--set", "splunk.discovery.receivers.postgresql.config.tls::insecure=true", + //Disabling postgresql.backends metric that doesn't dependably show up in metrics during testing + "--set", "splunk.discovery.receivers.postgresql.config.metrics::postgresql.backends::enabled=false", + }, + Env: []corev1.EnvVar{ + {Name: "PG_USERNAME", Value: "test_user"}, + {Name: "PG_PASSWORD", Value: "test_password"}, + {Name: "OTLP_ENDPOINT", Value: otlpEndpoint}, + // Helpful for debugging + // {Name: "SPLUNK_DISCOVERY_DURATION", Value: "20s"}, + // {Name: "SPLUNK_DISCOVERY_LOG_LEVEL", Value: "debug"}, + }, + Name: "otel-collector", + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config-map-volume", MountPath: "/config", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "config-map-volume", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: configMap, + }, + Items: []corev1.KeyToPath{ + { + Key: "config", + Path: "config.yaml", + }, + }, + }, + }, + }, + }, + } +} diff --git a/tests/receivers/postgresql/testdata/otlp_exporter.yaml b/tests/receivers/postgresql/testdata/otlp_exporter.yaml new file mode 100644 index 0000000000..fa00f3c646 --- /dev/null +++ b/tests/receivers/postgresql/testdata/otlp_exporter.yaml @@ -0,0 +1,13 @@ +exporters: + otlp: + endpoint: "${OTLP_ENDPOINT}" + tls: + insecure: true + +service: + telemetry: + logs: + level: debug + pipelines: + metrics: + exporters: [otlp] diff --git a/tests/receivers/postgresql/testdata/resource_metrics/all_k8s.yaml b/tests/receivers/postgresql/testdata/resource_metrics/all_k8s.yaml new file mode 100644 index 0000000000..cab04d5af3 --- /dev/null +++ b/tests/receivers/postgresql/testdata/resource_metrics/all_k8s.yaml @@ -0,0 +1,125 @@ +resource_metrics: + - attributes: + postgresql.database.name: + k8s.namespace.name: + k8s.pod.name: + k8s.pod.uid: + scope_metrics: + - instrumentation_scope: + name: otelcol/postgresqlreceiver + version: + attributes: {} + metrics: + - name: postgresql.commits + type: IntMonotonicCumulativeSum + - name: postgresql.db_size + type: IntNonmonotonicCumulativeSum + - name: postgresql.rollbacks + type: IntMonotonicCumulativeSum + - name: postgresql.table.count + type: IntNonmonotonicCumulativeSum + - attributes: + postgresql.database.name: + postgresql.index.name: + postgresql.table.name: + k8s.namespace.name: + k8s.pod.name: + k8s.pod.uid: + scope_metrics: + - instrumentation_scope: + name: otelcol/postgresqlreceiver + version: + attributes: {} + metrics: + - name: postgresql.index.scans + type: IntMonotonicCumulativeSum + - name: postgresql.index.size + type: IntGauge + - attributes: + postgresql.database.name: + postgresql.table.name: + k8s.namespace.name: + k8s.pod.name: + k8s.pod.uid: + scope_metrics: + - instrumentation_scope: + name: otelcol/postgresqlreceiver + version: + attributes: {} + metrics: + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: heap_read + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: heap_hit + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: idx_read + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: idx_hit + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: toast_hit + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: tidx_read + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: tidx_hit + - name: postgresql.operations + type: IntMonotonicCumulativeSum + attributes: + operation: del + - name: postgresql.operations + type: IntMonotonicCumulativeSum + attributes: + operation: upd + - name: postgresql.operations + type: IntMonotonicCumulativeSum + attributes: + operation: hot_upd + - name: postgresql.rows + type: IntNonmonotonicCumulativeSum + attributes: + state: dead + - name: postgresql.rows + type: IntNonmonotonicCumulativeSum + attributes: + state: live + - name: postgresql.table.size + type: IntNonmonotonicCumulativeSum + - name: postgresql.table.vacuum.count + type: IntMonotonicCumulativeSum + - attributes: + k8s.namespace.name: + k8s.pod.name: + k8s.pod.uid: + scope_metrics: + - instrumentation_scope: + name: otelcol/postgresqlreceiver + version: + attributes: {} + metrics: + - name: postgresql.bgwriter.buffers.allocated + type: IntMonotonicCumulativeSum + - name: postgresql.bgwriter.buffers.writes + type: IntMonotonicCumulativeSum + - name: postgresql.bgwriter.checkpoint.count + type: IntMonotonicCumulativeSum + - name: postgresql.bgwriter.duration + type: DoubleMonotonicCumulativeSum + - name: postgresql.bgwriter.maxwritten + type: IntMonotonicCumulativeSum + - name: postgresql.connection.max + type: IntGauge + - name: postgresql.database.count + type: IntNonmonotonicCumulativeSum \ No newline at end of file diff --git a/tests/receivers/postgresql/testdata/resource_metrics/bundled.yaml b/tests/receivers/postgresql/testdata/resource_metrics/bundled.yaml new file mode 100644 index 0000000000..c56cc1e1e0 --- /dev/null +++ b/tests/receivers/postgresql/testdata/resource_metrics/bundled.yaml @@ -0,0 +1,121 @@ +resource_metrics: + - attributes: + container.image.name: + container.name: + postgresql.database.name: + scope_metrics: + - instrumentation_scope: + attributes: {} + name: otelcol/postgresqlreceiver + version: + metrics: + - name: postgresql.commits + type: IntMonotonicCumulativeSum + - name: postgresql.db_size + type: IntNonmonotonicCumulativeSum + - name: postgresql.rollbacks + type: IntMonotonicCumulativeSum + - name: postgresql.table.count + type: IntNonmonotonicCumulativeSum + - attributes: + container.image.name: + container.name: + postgresql.database.name: + postgresql.index.name: + postgresql.table.name: + scope_metrics: + - instrumentation_scope: + attributes: {} + name: otelcol/postgresqlreceiver + version: + metrics: + - name: postgresql.index.scans + type: IntMonotonicCumulativeSum + - name: postgresql.index.size + type: IntGauge + - attributes: + container.image.name: + container.name: + postgresql.database.name: + postgresql.table.name: + scope_metrics: + - instrumentation_scope: + attributes: {} + name: otelcol/postgresqlreceiver + version: + metrics: + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: heap_read + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: heap_hit + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: idx_read + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: idx_hit + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: toast_hit + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: tidx_read + - name: postgresql.blocks_read + type: IntMonotonicCumulativeSum + attributes: + source: tidx_hit + - name: postgresql.operations + type: IntMonotonicCumulativeSum + attributes: + operation: del + - name: postgresql.operations + type: IntMonotonicCumulativeSum + attributes: + operation: upd + - name: postgresql.operations + type: IntMonotonicCumulativeSum + attributes: + operation: hot_upd + - name: postgresql.rows + type: IntNonmonotonicCumulativeSum + attributes: + state: dead + - name: postgresql.rows + type: IntNonmonotonicCumulativeSum + attributes: + state: live + - name: postgresql.table.size + type: IntNonmonotonicCumulativeSum + - name: postgresql.table.vacuum.count + type: IntMonotonicCumulativeSum + - attributes: + container.image.name: + container.name: + scope_metrics: + - instrumentation_scope: + attributes: {} + name: otelcol/postgresqlreceiver + version: + metrics: + - name: postgresql.bgwriter.buffers.allocated + type: IntMonotonicCumulativeSum + - name: postgresql.bgwriter.buffers.writes + type: IntMonotonicCumulativeSum + - name: postgresql.bgwriter.checkpoint.count + type: IntMonotonicCumulativeSum + - name: postgresql.bgwriter.duration + type: DoubleMonotonicCumulativeSum + - name: postgresql.bgwriter.maxwritten + type: IntMonotonicCumulativeSum + - name: postgresql.connection.max + type: IntGauge + - name: postgresql.database.count + type: IntNonmonotonicCumulativeSum \ No newline at end of file diff --git a/tests/receivers/smartagent/postgresql/bundled_test.go b/tests/receivers/smartagent/postgresql/bundled_test.go index d21ef22aff..07d72c1d80 100644 --- a/tests/receivers/smartagent/postgresql/bundled_test.go +++ b/tests/receivers/smartagent/postgresql/bundled_test.go @@ -336,6 +336,7 @@ func (cluster testCluster) daemonSet(namespace, serviceAccount, configMap string "--set", "splunk.discovery.receivers.smartagent/postgresql.config.params::password='${env:PG_PASSWORD}'", "--set", "splunk.discovery.receivers.smartagent/postgresql.config.masterDBName=test_db", "--set", `splunk.discovery.receivers.smartagent/postgresql.config.extraMetrics=["*"]`, + "--set", `splunk.discovery.receivers.smartagent/postgresql.enabled=true`, }, Env: []corev1.EnvVar{ {Name: "PG_USERNAME", Value: "test_user"},