From 349ebd187dea9f5f3204ebe1dc2ac15ee5b92a6b Mon Sep 17 00:00:00 2001 From: Brian Derr Date: Tue, 10 Dec 2024 15:53:10 -0800 Subject: [PATCH 1/9] [exporter/splunkhecexporter] Add `OtelAttrsToHec` struct and related config fields (#35476) **Description:** Adding a new config fields `otel_attrs_to_hec_metadata/*` to replace the `hec_metadata_to_otel_attrs/*` config fields. **Link to tracking Issue:** fixes #35092 **Testing:** No new tests added, modified existing tests. **Documentation:** Adding field-level description to exporter README.md. --- .chloggen/iss-35092.yaml | 28 +++++ exporter/splunkhecexporter/README.md | 8 ++ exporter/splunkhecexporter/config.go | 5 + exporter/splunkhecexporter/config_test.go | 6 + exporter/splunkhecexporter/factory.go | 6 + .../splunkhecexporter/logdata_to_splunk.go | 39 +++++- .../logdata_to_splunk_test.go | 116 ++++++++++++++++-- .../splunkhecexporter/testdata/config.yaml | 5 + internal/splunk/common.go | 10 ++ 9 files changed, 208 insertions(+), 15 deletions(-) create mode 100644 .chloggen/iss-35092.yaml diff --git a/.chloggen/iss-35092.yaml b/.chloggen/iss-35092.yaml new file mode 100644 index 000000000000..79fddc3f42dd --- /dev/null +++ b/.chloggen/iss-35092.yaml @@ -0,0 +1,28 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: deprecation + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: splunkhecexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add `otel_attrs_to_hec_metadata/*` config fields to replace `hec_metadata_to_otel_attrs/*` fields. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35092] + +# (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: | + `otel_attrs_to_hec_metadata/*` config fields will replace the `hec_metadata_to_otel_attrs/*` fields in a later release. + +# 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/exporter/splunkhecexporter/README.md b/exporter/splunkhecexporter/README.md index 5db67fce7696..29a25f8ac7fe 100644 --- a/exporter/splunkhecexporter/README.md +++ b/exporter/splunkhecexporter/README.md @@ -54,10 +54,18 @@ The following configuration options can also be configured: - `health_path` (default = '/services/collector/health'): The path reporting [health checks](https://docs.splunk.com/Documentation/Splunk/9.0.1/RESTREF/RESTinput#services.2Fcollector.2Fhealth). - `health_check_enabled` (default = false): Whether to perform Splunk HEC Health Check during the exporter's startup. - `export_raw` (default = false): send only the log's body, targeting a Splunk HEC raw endpoint. +- `otel_attrs_to_hec_metadata/source` (default = 'com.splunk.source'): Specifies the mapping of a specific unified model attribute value to the standard source field of a HEC event. +- `otel_attrs_to_hec_metadata/sourcetype` (default = 'com.splunk.sourcetype'): Specifies the mapping of a specific unified model attribute value to the standard sourcetype field of a HEC event. +- `otel_attrs_to_hec_metadata/index` (default = 'com.splunk.index'): Specifies the mapping of a specific unified model attribute value to the standard index field of a HEC event. +- `otel_attrs_to_hec_metadata/host` (default = 'host.name'): Specifies the mapping of a specific unified model attribute value to the standard host field and the `host.name` field of a HEC event. - `hec_metadata_to_otel_attrs/source` (default = 'com.splunk.source'): Specifies the mapping of a specific unified model attribute value to the standard source field of a HEC event. + **Deprecated** (v0.116.0): prefer `otel_attrs_to_hec_metadata/source`. - `hec_metadata_to_otel_attrs/sourcetype` (default = 'com.splunk.sourcetype'): Specifies the mapping of a specific unified model attribute value to the standard sourcetype field of a HEC event. + **Deprecated** (v0.116.0): prefer `otel_attrs_to_hec_metadata/sourcetype`. - `hec_metadata_to_otel_attrs/index` (default = 'com.splunk.index'): Specifies the mapping of a specific unified model attribute value to the standard index field of a HEC event. + **Deprecated** (v0.116.0): prefer `otel_attrs_to_hec_metadata/index`. - `hec_metadata_to_otel_attrs/host` (default = 'host.name'): Specifies the mapping of a specific unified model attribute value to the standard host field and the `host.name` field of a HEC event. + **Deprecated** (v0.116.0): prefer `otel_attrs_to_hec_metadata/host`. - `otel_to_hec_fields/severity_text` (default = `otel.log.severity.text`): Specifies the name of the field to map the severity text field of log events. - `otel_to_hec_fields/severity_number` (default = `otel.log.severity.number`): Specifies the name of the field to map the severity number field of log events. - `otel_to_hec_fields/name` (default = `"otel.log.name`): Specifies the name of the field to map the name field of log events. diff --git a/exporter/splunkhecexporter/config.go b/exporter/splunkhecexporter/config.go index 0d8c3e4a3e0f..9e435af573fb 100644 --- a/exporter/splunkhecexporter/config.go +++ b/exporter/splunkhecexporter/config.go @@ -116,7 +116,12 @@ type Config struct { // App version is used to track telemetry information for Splunk App's using HEC by App version. Defaults to the current OpenTelemetry Collector Contrib build version. SplunkAppVersion string `mapstructure:"splunk_app_version"` + + // OtelAttrsToHec creates a mapping from attributes to HEC specific metadata: source, sourcetype, index and host. + OtelAttrsToHec splunk.HecToOtelAttrs `mapstructure:"otel_attrs_to_hec_metadata"` + // HecToOtelAttrs creates a mapping from attributes to HEC specific metadata: source, sourcetype, index and host. + // Deprecated: [v0.113.0] Use OtelAttrsToHec instead. HecToOtelAttrs splunk.HecToOtelAttrs `mapstructure:"hec_metadata_to_otel_attrs"` // HecFields creates a mapping from attributes to HEC fields. HecFields OtelToHecFields `mapstructure:"otel_to_hec_fields"` diff --git a/exporter/splunkhecexporter/config_test.go b/exporter/splunkhecexporter/config_test.go index a8290bd7eab6..a9a2959f4a00 100644 --- a/exporter/splunkhecexporter/config_test.go +++ b/exporter/splunkhecexporter/config_test.go @@ -102,6 +102,12 @@ func TestLoadConfig(t *testing.T) { MaxSizeItems: 10, }, }, + OtelAttrsToHec: splunk.HecToOtelAttrs{ + Source: "mysource", + SourceType: "mysourcetype", + Index: "myindex", + Host: "myhost", + }, HecToOtelAttrs: splunk.HecToOtelAttrs{ Source: "mysource", SourceType: "mysourcetype", diff --git a/exporter/splunkhecexporter/factory.go b/exporter/splunkhecexporter/factory.go index a5d82775b28e..38f8d570d147 100644 --- a/exporter/splunkhecexporter/factory.go +++ b/exporter/splunkhecexporter/factory.go @@ -86,6 +86,12 @@ func createDefaultConfig() component.Config { MaxContentLengthMetrics: defaultContentLengthMetricsLimit, MaxContentLengthTraces: defaultContentLengthTracesLimit, MaxEventSize: defaultMaxEventSize, + OtelAttrsToHec: splunk.HecToOtelAttrs{ + Source: splunk.DefaultSourceLabel, + SourceType: splunk.DefaultSourceTypeLabel, + Index: splunk.DefaultIndexLabel, + Host: conventions.AttributeHostName, + }, HecToOtelAttrs: splunk.HecToOtelAttrs{ Source: splunk.DefaultSourceLabel, SourceType: splunk.DefaultSourceTypeLabel, diff --git a/exporter/splunkhecexporter/logdata_to_splunk.go b/exporter/splunkhecexporter/logdata_to_splunk.go index a89ff89ec8f6..96ad419a2a5b 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk.go +++ b/exporter/splunkhecexporter/logdata_to_splunk.go @@ -23,6 +23,33 @@ const ( traceIDFieldKey = "trace_id" ) +// copyOtelAttrs copies values from HecToOtelAttrs to OtelAttrsToHec struct. +func copyOtelAttrs(config *Config) { + defaultCfg := createDefaultConfig().(*Config) + if config.OtelAttrsToHec.Equal(defaultCfg.OtelAttrsToHec) { + if !config.HecToOtelAttrs.Equal(defaultCfg.HecToOtelAttrs) { + // Copy settings to ease deprecation of HecToOtelAttrs. + config.OtelAttrsToHec = config.HecToOtelAttrs + } + } else { + if !config.HecToOtelAttrs.Equal(defaultCfg.HecToOtelAttrs) { + // Replace all default fields in OtelAttrsToHec. + if config.OtelAttrsToHec.Source == defaultCfg.OtelAttrsToHec.Source { + config.OtelAttrsToHec.Source = config.HecToOtelAttrs.Source + } + if config.OtelAttrsToHec.SourceType == defaultCfg.OtelAttrsToHec.SourceType { + config.OtelAttrsToHec.SourceType = config.HecToOtelAttrs.SourceType + } + if config.OtelAttrsToHec.Index == defaultCfg.OtelAttrsToHec.Index { + config.OtelAttrsToHec.Index = config.HecToOtelAttrs.Index + } + if config.OtelAttrsToHec.Host == defaultCfg.OtelAttrsToHec.Host { + config.OtelAttrsToHec.Host = config.HecToOtelAttrs.Host + } + } + } +} + func mapLogRecordToSplunkEvent(res pcommon.Resource, lr plog.LogRecord, config *Config) *splunk.Event { body := lr.Body().AsRaw() if body == nil || body == "" { @@ -30,15 +57,19 @@ func mapLogRecordToSplunkEvent(res pcommon.Resource, lr plog.LogRecord, config * return nil } + // Manage the deprecation of HecToOtelAttrs config parameters. + // TODO: remove this once HecToOtelAttrs is removed from Config. + copyOtelAttrs(config) + host := unknownHostName source := config.Source sourcetype := config.SourceType index := config.Index fields := map[string]any{} - sourceKey := config.HecToOtelAttrs.Source - sourceTypeKey := config.HecToOtelAttrs.SourceType - indexKey := config.HecToOtelAttrs.Index - hostKey := config.HecToOtelAttrs.Host + sourceKey := config.OtelAttrsToHec.Source + sourceTypeKey := config.OtelAttrsToHec.SourceType + indexKey := config.OtelAttrsToHec.Index + hostKey := config.OtelAttrsToHec.Host severityTextKey := config.HecFields.SeverityText severityNumberKey := config.HecFields.SeverityNumber if spanID := lr.SpanID(); !spanID.IsEmpty() { diff --git a/exporter/splunkhecexporter/logdata_to_splunk_test.go b/exporter/splunkhecexporter/logdata_to_splunk_test.go index 6c4303f48c83..a8bd780c4fbb 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk_test.go +++ b/exporter/splunkhecexporter/logdata_to_splunk_test.go @@ -14,6 +14,100 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk" ) +func Test_copyOtelAttrs(t *testing.T) { + tests := []struct { + name string + configDataFn func() *Config + wantConfigDataFn func() *Config + }{ + { + name: "defaults", + configDataFn: func() *Config { + return createDefaultConfig().(*Config) + }, + wantConfigDataFn: func() *Config { + return createDefaultConfig().(*Config) + }, + }, + { + name: "override hec_metadata_to_otel_attrs", + configDataFn: func() *Config { + cfg := createDefaultConfig().(*Config) + + cfg.HecToOtelAttrs.Index = "testIndex" + cfg.HecToOtelAttrs.Source = "testSource" + cfg.HecToOtelAttrs.SourceType = "testSourceType" + cfg.HecToOtelAttrs.Host = "testHost" + + return cfg + }, + wantConfigDataFn: func() *Config { + cfg := createDefaultConfig().(*Config) + + cfg.HecToOtelAttrs.Index = "testIndex" + cfg.HecToOtelAttrs.Source = "testSource" + cfg.HecToOtelAttrs.SourceType = "testSourceType" + cfg.HecToOtelAttrs.Host = "testHost" + + cfg.OtelAttrsToHec.Index = "testIndex" + cfg.OtelAttrsToHec.Source = "testSource" + cfg.OtelAttrsToHec.SourceType = "testSourceType" + cfg.OtelAttrsToHec.Host = "testHost" + + return cfg + }, + }, + { + name: "partial otel_attrs_to_hec_metadata", + configDataFn: func() *Config { + cfg := createDefaultConfig().(*Config) + + cfg.OtelAttrsToHec.Source = "testSource" + cfg.OtelAttrsToHec.Index = "testIndex" + + return cfg + }, + wantConfigDataFn: func() *Config { + cfg := createDefaultConfig().(*Config) + + cfg.OtelAttrsToHec.Source = "testSource" + cfg.OtelAttrsToHec.Index = "testIndex" + + return cfg + }, + }, + { + name: "prefer otel_attrs_to_hec_metadata", + configDataFn: func() *Config { + cfg := createDefaultConfig().(*Config) + + cfg.HecToOtelAttrs.Index = "hecIndex" + + cfg.OtelAttrsToHec.Index = "otelIndex" + + return cfg + }, + wantConfigDataFn: func() *Config { + cfg := createDefaultConfig().(*Config) + + cfg.HecToOtelAttrs.Index = "hecIndex" + + cfg.OtelAttrsToHec.Index = "otelIndex" + + return cfg + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := tt.configDataFn() + copyOtelAttrs(cfg) + assert.Equal(t, tt.wantConfigDataFn(), cfg) + }) + } +} + func Test_mapLogRecordToSplunkEvent(t *testing.T) { ts := pcommon.Timestamp(123) @@ -153,18 +247,18 @@ func Test_mapLogRecordToSplunkEvent(t *testing.T) { }, logResourceFn: pcommon.NewResource, configDataFn: func() *Config { - return &Config{ - HecToOtelAttrs: splunk.HecToOtelAttrs{ - Source: "mysource", - SourceType: "mysourcetype", - Index: "myindex", - Host: "myhost", - }, - HecFields: OtelToHecFields{ - SeverityNumber: "myseveritynum", - SeverityText: "myseverity", - }, + config := createDefaultConfig().(*Config) + config.HecToOtelAttrs = splunk.HecToOtelAttrs{ + Source: "mysource", + SourceType: "mysourcetype", + Index: "myindex", + Host: "myhost", + } + config.HecFields = OtelToHecFields{ + SeverityNumber: "myseveritynum", + SeverityText: "myseverity", } + return config }, wantSplunkEvents: []*splunk.Event{ func() *splunk.Event { diff --git a/exporter/splunkhecexporter/testdata/config.yaml b/exporter/splunkhecexporter/testdata/config.yaml index 54b7040dbf8d..5e6afc0e058b 100644 --- a/exporter/splunkhecexporter/testdata/config.yaml +++ b/exporter/splunkhecexporter/testdata/config.yaml @@ -33,6 +33,11 @@ splunk_hec/allsettings: max_size_items: 10 splunk_app_name: "OpenTelemetry-Collector Splunk Exporter" splunk_app_version: "v0.0.1" + otel_attrs_to_hec_metadata: + source: "mysource" + sourcetype: "mysourcetype" + index: "myindex" + host: "myhost" hec_metadata_to_otel_attrs: source: "mysource" sourcetype: "mysourcetype" diff --git a/internal/splunk/common.go b/internal/splunk/common.go index c7cc3c2ecb40..e57ecaf53e15 100644 --- a/internal/splunk/common.go +++ b/internal/splunk/common.go @@ -139,6 +139,16 @@ type HecToOtelAttrs struct { Host string `mapstructure:"host"` } +func (h HecToOtelAttrs) Equal(o HecToOtelAttrs) bool { + if h.Host != o.Host || + h.Source != o.Source || + h.SourceType != o.SourceType || + h.Index != o.Index { + return false + } + return true +} + type AckRequest struct { Acks []uint64 `json:"acks"` } From 421d710d3d8ceb28e375f57109ae976423713f9d Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Tue, 10 Dec 2024 19:21:57 -0800 Subject: [PATCH 2/9] [extension/jsonlogencoding] Support marshaling multiple logs (#36156) #### Description Change how logs are marshaled. Instead of marshaling just the first log, marshal all logs into a JSON array. #### Link to tracking issue Fixes #34064 --- .chloggen/consume_multiple_records.yaml | 27 ++++++++++ .../jsonlogencodingextension/extension.go | 54 ++++++++++--------- .../encoding/jsonlogencodingextension/go.mod | 3 +- .../encoding/jsonlogencodingextension/go.sum | 2 + .../jsonlogencodingextension/json_test.go | 4 +- 5 files changed, 63 insertions(+), 27 deletions(-) create mode 100644 .chloggen/consume_multiple_records.yaml diff --git a/.chloggen/consume_multiple_records.yaml b/.chloggen/consume_multiple_records.yaml new file mode 100644 index 000000000000..6ec779a071e9 --- /dev/null +++ b/.chloggen/consume_multiple_records.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: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: jsonlogencodingextension + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Change how logs are marshaled. Instead of marshaling just the first log, marshal all logs into a JSON array. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [34064] + +# (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: [] diff --git a/extension/encoding/jsonlogencodingextension/extension.go b/extension/encoding/jsonlogencodingextension/extension.go index 266c6d91aa60..18575859ce7f 100644 --- a/extension/encoding/jsonlogencodingextension/extension.go +++ b/extension/encoding/jsonlogencodingextension/extension.go @@ -6,9 +6,8 @@ package jsonlogencodingextension // import "github.com/open-telemetry/openteleme import ( "context" "fmt" - "time" - jsoniter "github.com/json-iterator/go" + "github.com/goccy/go-json" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" @@ -29,38 +28,45 @@ func (e *jsonLogExtension) MarshalLogs(ld plog.Logs) ([]byte, error) { if e.config.(*Config).Mode == JSONEncodingModeBodyWithInlineAttributes { return e.logProcessor(ld) } - logRecord := ld.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0).Body() - var raw map[string]any - switch logRecord.Type() { - case pcommon.ValueTypeMap: - raw = logRecord.Map().AsRaw() - default: - return nil, fmt.Errorf("marshal: expected 'Map' found '%v'", logRecord.Type().String()) - } - buf, err := jsoniter.Marshal(raw) - if err != nil { - return nil, err + logs := make([]map[string]any, 0, ld.LogRecordCount()) + + rls := ld.ResourceLogs() + for i := 0; i < rls.Len(); i++ { + rl := rls.At(i) + sls := rl.ScopeLogs() + for j := 0; j < sls.Len(); j++ { + sl := sls.At(j) + logSlice := sl.LogRecords() + for k := 0; k < logSlice.Len(); k++ { + log := logSlice.At(k) + switch log.Body().Type() { + case pcommon.ValueTypeMap: + logs = append(logs, log.Body().Map().AsRaw()) + default: + return nil, fmt.Errorf("marshal: expected 'Map' found '%v'", log.Body().Type()) + } + } + } } - return buf, nil + return json.Marshal(logs) } func (e *jsonLogExtension) UnmarshalLogs(buf []byte) (plog.Logs, error) { p := plog.NewLogs() // get json logs from the buffer - jsonVal := map[string]any{} - if err := jsoniter.Unmarshal(buf, &jsonVal); err != nil { + var jsonVal []map[string]any + if err := json.Unmarshal(buf, &jsonVal); err != nil { return p, err } - // create a new log record - logRecords := p.ResourceLogs().AppendEmpty().ScopeLogs().AppendEmpty().LogRecords().AppendEmpty() - logRecords.SetObservedTimestamp(pcommon.NewTimestampFromTime(time.Now())) - - // Set the unmarshaled jsonVal as the body of the log record - if err := logRecords.Body().SetEmptyMap().FromRaw(jsonVal); err != nil { - return p, err + sl := p.ResourceLogs().AppendEmpty().ScopeLogs().AppendEmpty() + for _, r := range jsonVal { + if err := sl.LogRecords().AppendEmpty().Body().SetEmptyMap().FromRaw(r); err != nil { + return p, err + } } + return p, nil } @@ -96,7 +102,7 @@ func (e *jsonLogExtension) logProcessor(ld plog.Logs) ([]byte, error) { } } - return jsoniter.Marshal(logs) + return json.Marshal(logs) } type logBody struct { diff --git a/extension/encoding/jsonlogencodingextension/go.mod b/extension/encoding/jsonlogencodingextension/go.mod index 1252235f2a77..33ef63938cea 100644 --- a/extension/encoding/jsonlogencodingextension/go.mod +++ b/extension/encoding/jsonlogencodingextension/go.mod @@ -3,7 +3,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/extension/encod go 1.22.0 require ( - github.com/json-iterator/go v1.1.12 + github.com/goccy/go-json v0.10.3 github.com/open-telemetry/opentelemetry-collector-contrib/extension/encoding v0.115.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8 @@ -22,6 +22,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // 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.2 // indirect diff --git a/extension/encoding/jsonlogencodingextension/go.sum b/extension/encoding/jsonlogencodingextension/go.sum index 15de1d72e6d4..f80d74fe7739 100644 --- a/extension/encoding/jsonlogencodingextension/go.sum +++ b/extension/encoding/jsonlogencodingextension/go.sum @@ -9,6 +9,8 @@ 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.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 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= diff --git a/extension/encoding/jsonlogencodingextension/json_test.go b/extension/encoding/jsonlogencodingextension/json_test.go index e3fffa6869a4..38daf73916dc 100644 --- a/extension/encoding/jsonlogencodingextension/json_test.go +++ b/extension/encoding/jsonlogencodingextension/json_test.go @@ -17,7 +17,7 @@ func TestMarshalUnmarshal(t *testing.T) { Mode: JSONEncodingModeBody, }, } - json := `{"example":"example valid json to test that the unmarshaler is correctly returning a plog value"}` + json := `[{"example":"example valid json to test that the unmarshaler is correctly returning a plog value"}]` ld, err := e.UnmarshalLogs([]byte(json)) assert.NoError(t, err) assert.Equal(t, 1, ld.LogRecordCount()) @@ -47,7 +47,7 @@ func TestInvalidUnmarshal(t *testing.T) { }, } _, err := e.UnmarshalLogs([]byte("NOT A JSON")) - assert.ErrorContains(t, err, "ReadMapCB: expect { or n, but found N") + assert.ErrorContains(t, err, "json: slice unexpected end of JSON input") } func TestPrettyLogProcessor(t *testing.T) { From 11fe1366a0596d4150aa94a1d6d6ab260670cbb0 Mon Sep 17 00:00:00 2001 From: Nicolas <69248573+niwoerner@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:08:10 +0100 Subject: [PATCH 3/9] [chore] run markdown-link-check on version change (#36645) #### Description This PR will run the "markdown-link-check" tool if the version of the tool changes even if no markdown files were updated. This will help to confirm that "markdown-link-check" is still working as expected after a version update/pin. ~~If 1+ *.md files gets changed AND the version gets updated, it will only check the updated files to keep the same behaviour as before.~~ ~~If only the version gets updated it will test all existing *.md files.~~ **EDIT:** After feedback and suggestion from @mowies (thank you again! :) ), I switched to use a [predefined](https://github.com/tj-actions/changed-files) github action which checks markdown files changes AND if the `.github/workflows/check-links.yaml` changed. If the '.github/workflows/check-links.yaml' file, the markdown-link-check tool will test against all markdown files existing in the repo. This would catch a version change of the "markdown-link-check" tool and test it accordingly. If 1+ *.md file gets changed while the `.github/workflows/check-links.yaml` file does **not** change, it will only test the changed files to keep the same behaviour like before. /cc @mx-psi @mowies @ChrsMark #### Link to tracking issue Fixes https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/36588 --------- Co-authored-by: Christos Markou --- .github/workflows/check-links.yaml | 29 +++++++++++++------- .github/workflows/check_links_config.json | 3 ++ cmd/telemetrygen/README.md | 2 +- exporter/azuremonitorexporter/conventions.go | 2 +- pkg/stanza/docs/operators/tcp_input.md | 2 +- pkg/translator/prometheus/README.md | 2 +- pkg/translator/prometheus/normalize_name.go | 2 +- receiver/azureblobreceiver/README.md | 2 +- receiver/tcplogreceiver/README.md | 2 +- 9 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.github/workflows/check-links.yaml b/.github/workflows/check-links.yaml index 7b0dccb45d4a..69fe83dd18c6 100644 --- a/.github/workflows/check-links.yaml +++ b/.github/workflows/check-links.yaml @@ -17,35 +17,44 @@ jobs: changedfiles: name: changed files runs-on: ubuntu-24.04 - env: - PR_HEAD: ${{ github.event.pull_request.head.sha }} if: ${{ github.actor != 'dependabot[bot]' }} outputs: - md: ${{ steps.changes.outputs.md }} + md_files: ${{ steps.changed-files.outputs.md_all_changed_files }} + yaml_files: ${{ steps.changed-files.outputs.yaml_all_changed_files }} # used to catch MD_LINK_CHECK_VERSION updates and runs check-links to confirm functionality steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get changed files - id: changes - run: | - echo "md=$(git diff --name-only --diff-filter=ACMRTUXB $(git merge-base origin/main $PR_HEAD) $PR_HEAD | grep .md$ | xargs)" >> $GITHUB_OUTPUT + id: changed-files + uses: tj-actions/changed-files@v45 + with: + files_yaml: | + md: + - '**.md' + yaml: + - .github/workflows/check-links.yaml + check-links: runs-on: ubuntu-24.04 needs: changedfiles - if: ${{needs.changedfiles.outputs.md}} + if: needs.changedfiles.outputs.md_files || needs.changedfiles.outputs.yaml_files steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install markdown-link-check run: npm install -g markdown-link-check@${{ env.MD_LINK_CHECK_VERSION }} - - name: Run markdown-link-check run: | + if [ -n "${{ needs.changedfiles.outputs.yaml_files }}" ]; then + md=$(find . -type f -name "*.md") + else + md="${{ needs.changedfiles.outputs.md_files }}" + fi + markdown-link-check \ --verbose \ --config .github/workflows/check_links_config.json \ - ${{needs.changedfiles.outputs.md}} \ + $md \ || { echo "Check that anchor links are lowercase"; exit 1; } diff --git a/.github/workflows/check_links_config.json b/.github/workflows/check_links_config.json index f48e49e6938e..299206c09254 100644 --- a/.github/workflows/check_links_config.json +++ b/.github/workflows/check_links_config.json @@ -11,6 +11,9 @@ }, { "pattern": "^#" + }, + { + "pattern": "https://blogs\\.oracle\\.com/developers/post/connecting-a-go-application-to-oracle-database" } ], "aliveStatusCodes": [429, 200] diff --git a/cmd/telemetrygen/README.md b/cmd/telemetrygen/README.md index a17261219018..eeed5c0c0fa5 100644 --- a/cmd/telemetrygen/README.md +++ b/cmd/telemetrygen/README.md @@ -32,7 +32,7 @@ You can build locally the Docker image with: make docker-telemetrygen ``` -Using github actions, we also push a docker image on commit to main or on release to this [Github docker registry](https://github.com/open-telemetry/opentelemetry-collector-contrib/pkgs/container/opentelemetry-collector-contrib%2Ftelemetrygen). +Using github actions, we also push a docker image on commit to main or on release to this [Github docker registry](https://github.com/orgs/open-telemetry/packages/container/package/opentelemetry-collector-contrib%2Ftelemetrygen). ## Running diff --git a/exporter/azuremonitorexporter/conventions.go b/exporter/azuremonitorexporter/conventions.go index e764a74f33fc..3e4ce211e7a4 100644 --- a/exporter/azuremonitorexporter/conventions.go +++ b/exporter/azuremonitorexporter/conventions.go @@ -22,7 +22,7 @@ const ( // NetworkAttributes is the set of known network attributes type NetworkAttributes struct { - // see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#general-network-connection-attributes + // see https://github.com/open-telemetry/semantic-conventions/blob/main/docs/attributes-registry/network.md#network-attributes NetTransport string NetPeerIP string NetPeerPort int64 diff --git a/pkg/stanza/docs/operators/tcp_input.md b/pkg/stanza/docs/operators/tcp_input.md index 37197f49fcb1..c42a6c06eb9e 100644 --- a/pkg/stanza/docs/operators/tcp_input.md +++ b/pkg/stanza/docs/operators/tcp_input.md @@ -14,7 +14,7 @@ The `tcp_input` operator listens for logs on one or more TCP connections. The op | `attributes` | {} | A map of `key: value` pairs to add to the entry's attributes. | | `one_log_per_packet` | false | Skip log tokenization, set to true if logs contains one log per record and multiline is not used. This will improve performance. | | `resource` | {} | A map of `key: value` pairs to add to the entry's resource. | -| `add_attributes` | false | Adds `net.*` attributes according to [semantic convention][https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#general-network-connection-attributes]. | +| `add_attributes` | false | Adds `net.*` attributes according to [semantic convention][https://github.com/open-telemetry/semantic-conventions/blob/main/docs/attributes-registry/network.md#network-attributes]. | | `multiline` | | A `multiline` configuration block. See below for details. | | `preserve_leading_whitespaces` | false | Whether to preserve leading whitespaces. | | `preserve_trailing_whitespaces` | false | Whether to preserve trailing whitespaces. | diff --git a/pkg/translator/prometheus/README.md b/pkg/translator/prometheus/README.md index 4e88796845ff..57e008c7d234 100644 --- a/pkg/translator/prometheus/README.md +++ b/pkg/translator/prometheus/README.md @@ -1,6 +1,6 @@ # Prometheus Normalization -[OpenTelemetry's metric semantic convention](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/README.md) is not compatible with [Prometheus' own metrics naming convention](https://prometheus.io/docs/practices/naming/). This module provides centralized functions to convert OpenTelemetry metrics to Prometheus-compliant metrics. These functions are used by the following components for Prometheus: +[OpenTelemetry's metric semantic convention](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/metrics.md) is not compatible with [Prometheus' own metrics naming convention](https://prometheus.io/docs/practices/naming/). This module provides centralized functions to convert OpenTelemetry metrics to Prometheus-compliant metrics. These functions are used by the following components for Prometheus: * [prometheusreceiver](../../../receiver/prometheusreceiver/) * [prometheusexporter](../../../exporter/prometheusexporter/) diff --git a/pkg/translator/prometheus/normalize_name.go b/pkg/translator/prometheus/normalize_name.go index 00751e19530d..4131916f282d 100644 --- a/pkg/translator/prometheus/normalize_name.go +++ b/pkg/translator/prometheus/normalize_name.go @@ -13,7 +13,7 @@ import ( // The map to translate OTLP units to Prometheus units // OTLP metrics use the c/s notation as specified at https://ucum.org/ucum.html -// (See also https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/README.md#instrument-units) +// (See also https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/metrics.md#instrument-units) // Prometheus best practices for units: https://prometheus.io/docs/practices/naming/#base-units // OpenMetrics specification for units: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#units-and-base-units var unitMap = map[string]string{ diff --git a/receiver/azureblobreceiver/README.md b/receiver/azureblobreceiver/README.md index 13b75b038c6a..79b5c5c453c5 100644 --- a/receiver/azureblobreceiver/README.md +++ b/receiver/azureblobreceiver/README.md @@ -13,7 +13,7 @@ -This receiver reads logs and trace data from [Azure Blob Storage](https://azure.microsoft.com/services/storage/blobs/). +This receiver reads logs and trace data from [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs/). ## Configuration diff --git a/receiver/tcplogreceiver/README.md b/receiver/tcplogreceiver/README.md index 73a587d0dd5f..aef0778edcfe 100644 --- a/receiver/tcplogreceiver/README.md +++ b/receiver/tcplogreceiver/README.md @@ -24,7 +24,7 @@ Receives logs over TCP. | `attributes` | {} | A map of `key: value` pairs to add to the entry's attributes | | `one_log_per_packet` | false | Skip log tokenization, set to true if logs contains one log per record and multiline is not used. This will improve performance. | | `resource` | {} | A map of `key: value` pairs to add to the entry's resource | -| `add_attributes` | false | Adds `net.*` attributes according to [semantic convention][https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#general-network-connection-attributes] | +| `add_attributes` | false | Adds `net.*` attributes according to [semantic convention][https://github.com/open-telemetry/semantic-conventions/blob/main/docs/attributes-registry/network.md#network-attributes] | | `multiline` | | A `multiline` configuration block. See below for details | | `encoding` | `utf-8` | The encoding of the file being read. See the list of supported encodings below for available options | | `operators` | [] | An array of [operators](../../pkg/stanza/docs/operators/README.md#what-operators-are-available). See below for more details | From 28ede1db2f69b0bd4cf6c4ee4ec999dd62b007d2 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 11 Dec 2024 08:37:02 -0300 Subject: [PATCH 4/9] [chore] enabling replace `interface{}` by `any` (#36735) #### Description As we change the gofmt by gofumpt -> https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/36471, the rewrite rule that is responsible for change `interface{}` by `any` isn't working anymore. We noticed this problem here -> https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/36688#discussion_r1873836818. This PR tries to address this lack. --- .golangci.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 83f780bdc864..28bfb9ead30a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -80,13 +80,8 @@ linters-settings: revive: # minimal confidence for issues, default is 0.8 min-confidence: 0.8 - - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - rewrite-rules: - - pattern: interface{} - replacement: any + rules: + - name: use-any goimports: # put imports beginning with prefix after 3rd-party packages; From 338955cfabe0e4fd7b2138570ba3d4df5ab01a72 Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Wed, 11 Dec 2024 17:50:11 +0100 Subject: [PATCH 5/9] [opampextension] fix blocking agent shutdown due to unclosed channel (#36754) #### Description The changes introduced in https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/35892 seemed to have introduced some flakyness in the opampsupervisor e2e tests, as the shutdown of the opamp agent waits for the component health loop to end. Due to an unclosed channel within the opamp agent however, the agent does not properly shut down, and the supervisor runs into a timeout before ultimately sending a SIGKILL to the agent process. Closing the channel in the Shutdown method of the opamp extension fixes that and the agent gets shut down properly upon the reception of the SIGINT signal #### Link to tracking Issue: Fixes #36764 #### Testing This fixes the failing test mentioned in the issue (#36764) --------- Signed-off-by: Florian Bacher --- .chloggen/opamp-extension-shutdown.yaml | 27 +++++++++++++++++++++++++ extension/opampextension/opamp_agent.go | 3 +++ 2 files changed, 30 insertions(+) create mode 100644 .chloggen/opamp-extension-shutdown.yaml diff --git a/.chloggen/opamp-extension-shutdown.yaml b/.chloggen/opamp-extension-shutdown.yaml new file mode 100644 index 000000000000..370386f84889 --- /dev/null +++ b/.chloggen/opamp-extension-shutdown.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: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: opampextension + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Fix blocking agent shutdown due to unclosed channel + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36764] + +# (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: [] diff --git a/extension/opampextension/opamp_agent.go b/extension/opampextension/opamp_agent.go index c638e8727b05..3e26d407fd80 100644 --- a/extension/opampextension/opamp_agent.go +++ b/extension/opampextension/opamp_agent.go @@ -162,6 +162,9 @@ func (o *opampAgent) Shutdown(ctx context.Context) error { o.statusSubscriptionWg.Wait() o.componentHealthWg.Wait() + if o.componentStatusCh != nil { + close(o.componentStatusCh) + } o.logger.Debug("OpAMP agent shutting down...") if o.opampClient == nil { From 5fed9289f698cab9e42e92a906d946f1d745f027 Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Wed, 11 Dec 2024 08:50:35 -0800 Subject: [PATCH 6/9] [chore] fix typo in comment (#36785) --- internal/coreinternal/errorutil/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/coreinternal/errorutil/http.go b/internal/coreinternal/errorutil/http.go index 930f0fd51fcd..2d643042daaa 100644 --- a/internal/coreinternal/errorutil/http.go +++ b/internal/coreinternal/errorutil/http.go @@ -17,7 +17,7 @@ func HTTPError(w http.ResponseWriter, err error) { } func GetHTTPStatusCodeFromError(err error) int { - // See requirements for receviers + // See requirements for receivers // https://github.com/open-telemetry/opentelemetry-collector/blob/8e522ad950de6326a0841d7e1bef808bbc0d3537/receiver/doc.go#L10-L29 // See https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md#failures-1 From ab0f6a24521104e2e06c11673bc462aa4a5703e4 Mon Sep 17 00:00:00 2001 From: Mike Terhar Date: Wed, 11 Dec 2024 18:39:29 -0500 Subject: [PATCH 7/9] [receiver/libhoney] New receiver for libhoney (#36706) #### Description Creates a new receiver that accepts libhoney traffic as either logs or traces. This PR doesn't do the conversion yet. Just contains configurations and some factory components. #### Link to tracking issue #36693 --- .chloggen/initial_libhoneyreceiver.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/libhoneyreceiver/Makefile | 2 + receiver/libhoneyreceiver/README.md | 70 +++++++ receiver/libhoneyreceiver/config.go | 96 ++++++++++ receiver/libhoneyreceiver/doc.go | 6 + receiver/libhoneyreceiver/factory.go | 114 ++++++++++++ .../generated_component_test.go | 76 ++++++++ .../generated_package_test.go | 13 ++ receiver/libhoneyreceiver/go.mod | 76 ++++++++ receiver/libhoneyreceiver/go.sum | 174 ++++++++++++++++++ .../internal/metadata/generated_status.go | 17 ++ receiver/libhoneyreceiver/libhoney.go | 127 +++++++++++++ receiver/libhoneyreceiver/metadata.yaml | 8 + versions.yaml | 1 + 19 files changed, 812 insertions(+) create mode 100644 .chloggen/initial_libhoneyreceiver.yaml create mode 100644 receiver/libhoneyreceiver/Makefile create mode 100644 receiver/libhoneyreceiver/README.md create mode 100644 receiver/libhoneyreceiver/config.go create mode 100644 receiver/libhoneyreceiver/doc.go create mode 100644 receiver/libhoneyreceiver/factory.go create mode 100644 receiver/libhoneyreceiver/generated_component_test.go create mode 100644 receiver/libhoneyreceiver/generated_package_test.go create mode 100644 receiver/libhoneyreceiver/go.mod create mode 100644 receiver/libhoneyreceiver/go.sum create mode 100644 receiver/libhoneyreceiver/internal/metadata/generated_status.go create mode 100644 receiver/libhoneyreceiver/libhoney.go create mode 100644 receiver/libhoneyreceiver/metadata.yaml diff --git a/.chloggen/initial_libhoneyreceiver.yaml b/.chloggen/initial_libhoneyreceiver.yaml new file mode 100644 index 000000000000..39e8630f8cb8 --- /dev/null +++ b/.chloggen/initial_libhoneyreceiver.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: libhoneyreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Introduce the scaffolding of a new component, libhoneyreceiver + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36693] + +# (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: [] \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0fd8ec11925d..22e81e44700a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -244,6 +244,7 @@ receiver/k8sobjectsreceiver/ @open-telemetry/collector-cont receiver/kafkametricsreceiver/ @open-telemetry/collector-contrib-approvers @dmitryax receiver/kafkareceiver/ @open-telemetry/collector-contrib-approvers @pavolloffay @MovieStoreGuy receiver/kubeletstatsreceiver/ @open-telemetry/collector-contrib-approvers @dmitryax @TylerHelmuth @ChrsMark +receiver/libhoneyreceiver/ @open-telemetry/collector-contrib-approvers @TylerHelmuth receiver/lokireceiver/ @open-telemetry/collector-contrib-approvers @mar4uk receiver/memcachedreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski receiver/mongodbatlasreceiver/ @open-telemetry/collector-contrib-approvers @schmikei diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 40374a31674d..ea469c572bcb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -241,6 +241,7 @@ body: - receiver/kafka - receiver/kafkametrics - receiver/kubeletstats + - receiver/libhoney - receiver/loki - receiver/memcached - receiver/mongodb diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index aeee888ecb63..99f72f1c13b1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -235,6 +235,7 @@ body: - receiver/kafka - receiver/kafkametrics - receiver/kubeletstats + - receiver/libhoney - receiver/loki - receiver/memcached - receiver/mongodb diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml index 30152c26e1a0..c65b1b3fa089 100644 --- a/.github/ISSUE_TEMPLATE/other.yaml +++ b/.github/ISSUE_TEMPLATE/other.yaml @@ -235,6 +235,7 @@ body: - receiver/kafka - receiver/kafkametrics - receiver/kubeletstats + - receiver/libhoney - receiver/loki - receiver/memcached - receiver/mongodb diff --git a/.github/ISSUE_TEMPLATE/unmaintained.yaml b/.github/ISSUE_TEMPLATE/unmaintained.yaml index ab44912d5fe3..a6402ed0e246 100644 --- a/.github/ISSUE_TEMPLATE/unmaintained.yaml +++ b/.github/ISSUE_TEMPLATE/unmaintained.yaml @@ -240,6 +240,7 @@ body: - receiver/kafka - receiver/kafkametrics - receiver/kubeletstats + - receiver/libhoney - receiver/loki - receiver/memcached - receiver/mongodb diff --git a/receiver/libhoneyreceiver/Makefile b/receiver/libhoneyreceiver/Makefile new file mode 100644 index 000000000000..84677bc7e9cb --- /dev/null +++ b/receiver/libhoneyreceiver/Makefile @@ -0,0 +1,2 @@ +include ../../Makefile.Common + diff --git a/receiver/libhoneyreceiver/README.md b/receiver/libhoneyreceiver/README.md new file mode 100644 index 000000000000..a87c8735d5d0 --- /dev/null +++ b/receiver/libhoneyreceiver/README.md @@ -0,0 +1,70 @@ +# Libhoney Receiver + +| Status | | +| ------------- |-----------| +| Stability | [development]: traces, logs | +| Distributions | [] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Flibhoney%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Flibhoney) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Flibhoney%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Flibhoney) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@TylerHelmuth](https://www.github.com/TylerHelmuth) | + +[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development + + +### The purpose and use-cases of the new component + +The Libhoney receiver will accept data for either Trace or Logs signals that are emitted from applications that were +instrumented using [Libhoney](https://docs.honeycomb.io/send-data/logs/structured/libhoney/) libraries. + +## Configuration + +The configuration has 2 parts, One is the HTTP receiver configuration and the rest is about mapping attributes from the +freeform libhoney format into the more structured OpenTelemetry objects. + +### Example configuration for the component + +The following settings are required: + +- `http` + - `endpoint` must set an endpoint. Defaults to `127.0.0.1:8080` +- `resources`: if the `service.name` field is different, map it here. +- `scopes`: to get the `library.name` and `library.version` set in the scope section, set them here. +- `attributes`: if the other trace-related data have different keys, map them here, defaults are otlp-like field names. + +The following setting is required for refinery traffic since: + +- `auth_api`: should be set to `https://api.honeycomb.io` or a proxy that forwards to that host. + Some libhoney software checks `/1/auth` to get environment names so it needs to be passed through. + + +```yaml + libhoney: + http: + endpoint: 0.0.0.0:8088 + traces_url_paths: + - "/1/events" + - "/1/batch" + include_metadata: true + auth_api: https://api.honeycomb.io + resources: + service_name: service_name + scopes: + library_name: library.name + library_version: library.version + attributes: + trace_id: trace_id + parent_id: parent_id + span_id: span_id + name: name + error: error + spankind: span.kind + durationFields: + - duration_ms +``` + +### Telemetry data types supported + +It will subscribe to the Traces and Logs signals but accept traffic destined for either pipeline using one http receiver +component. Libhoney doesnot differentiate between the two so the receiver will identify which pipeline to deliver the +spans or log records to. + +No support for metrics since they'd look just like logs. diff --git a/receiver/libhoneyreceiver/config.go b/receiver/libhoneyreceiver/config.go new file mode 100644 index 000000000000..abfd6476dbd1 --- /dev/null +++ b/receiver/libhoneyreceiver/config.go @@ -0,0 +1,96 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package libhoneyreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver" + +import ( + "errors" + "fmt" + "net/url" + "path" + + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/confmap" +) + +// Config represents the receiver config settings within the collector's config.yaml +type Config struct { + HTTP *HTTPConfig `mapstructure:"http"` + AuthAPI string `mapstructure:"auth_api"` + Wrapper string `mapstructure:"wrapper"` + Resources ResourcesConfig `mapstructure:"resources"` + Scopes ScopesConfig `mapstructure:"scopes"` + Attributes AttributesConfig `mapstructure:"attributes"` +} + +type HTTPConfig struct { + *confighttp.ServerConfig `mapstructure:",squash"` + + // The URL path to receive traces on. If omitted "/" will be used. + TracesURLPaths []string `mapstructure:"traces_url_paths,omitempty"` +} + +type ResourcesConfig struct { + ServiceName string `mapstructure:"service_name"` +} + +type ScopesConfig struct { + LibraryName string `mapstructure:"library_name"` + LibraryVersion string `mapstructure:"library_version"` +} + +type AttributesConfig struct { + TraceID string `mapstructure:"trace_id"` + ParentID string `mapstructure:"parent_id"` + SpanID string `mapstructure:"span_id"` + Name string `mapstructure:"name"` + Error string `mapstructure:"error"` + SpanKind string `mapstructure:"spankind"` + DurationFields []string `mapstructure:"durationFields"` +} + +func (cfg *Config) Validate() error { + if cfg.HTTP == nil { + return errors.New("must specify at least one protocol when using the arbitrary JSON receiver") + } + return nil +} + +func (cfg *Config) Unmarshal(conf *confmap.Conf) error { + // first load the config normally + err := conf.Unmarshal(cfg) + if err != nil { + return err + } + + if !conf.IsSet("http") { + cfg.HTTP = nil + } else { + var err error + + for idx := range cfg.HTTP.TracesURLPaths { + if cfg.HTTP.TracesURLPaths[idx], err = sanitizeURLPath(cfg.HTTP.TracesURLPaths[idx]); err != nil { + return err + } + } + } + if cleanURL, err := url.Parse(cfg.AuthAPI); err != nil { + cfg.AuthAPI = cleanURL.String() + } else { + return err + } + + return nil +} + +func sanitizeURLPath(urlPath string) (string, error) { + u, err := url.Parse(urlPath) + if err != nil { + return "", fmt.Errorf("invalid HTTP URL path set for signal: %w", err) + } + + if !path.IsAbs(u.Path) { + u.Path = "/" + u.Path + } + return u.Path, nil +} diff --git a/receiver/libhoneyreceiver/doc.go b/receiver/libhoneyreceiver/doc.go new file mode 100644 index 000000000000..087590c1a4da --- /dev/null +++ b/receiver/libhoneyreceiver/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +package libhoneyreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver" diff --git a/receiver/libhoneyreceiver/factory.go b/receiver/libhoneyreceiver/factory.go new file mode 100644 index 000000000000..4d0d0fa25cfa --- /dev/null +++ b/receiver/libhoneyreceiver/factory.go @@ -0,0 +1,114 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package libhoneyreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver" + +import ( + "context" + "fmt" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver/internal/metadata" +) + +const ( + httpPort = 8080 +) + +var defaultTracesURLPaths = []string{"/events", "/event", "/batch"} + +// NewFactory creates a new OTLP receiver factory. +func NewFactory() receiver.Factory { + return receiver.NewFactory( + metadata.Type, + createDefaultConfig, + receiver.WithTraces(createTraces, metadata.TracesStability), + receiver.WithLogs(createLogs, metadata.LogsStability), + ) +} + +// createDefaultConfig creates the default configuration for receiver. +func createDefaultConfig() component.Config { + durationFieldsArr := []string{"duration_ms"} + endpointStr := fmt.Sprintf("localhost:%d", httpPort) + return &Config{ + HTTP: &HTTPConfig{ + ServerConfig: &confighttp.ServerConfig{ + Endpoint: endpointStr, + }, + TracesURLPaths: defaultTracesURLPaths, + }, + AuthAPI: "", + Resources: ResourcesConfig{ + ServiceName: "service.name", + }, + Scopes: ScopesConfig{ + LibraryName: "library.name", + LibraryVersion: "library.version", + }, + Attributes: AttributesConfig{ + TraceID: "trace.trace_id", + SpanID: "trace.span_id", + ParentID: "trace.parent_id", + Name: "name", + Error: "error", + SpanKind: "span.kind", + DurationFields: durationFieldsArr, + }, + } +} + +func createLogs( + _ context.Context, + set receiver.Settings, + cfg component.Config, + nextConsumer consumer.Logs, +) (receiver.Logs, error) { + oCfg := cfg.(*Config) + var err error + r := receivers.GetOrAdd( + oCfg, + func() (lh component.Component) { + lh, err = newLibhoneyReceiver(oCfg, &set) + return lh + }, + ) + + if err != nil { + return nil, err + } + + r.Unwrap().(*libhoneyReceiver).registerLogConsumer(nextConsumer) + return r, nil +} + +// createTraces creates a trace receiver based on provided config. +func createTraces( + _ context.Context, + set receiver.Settings, + cfg component.Config, + nextConsumer consumer.Traces, +) (receiver.Traces, error) { + oCfg := cfg.(*Config) + var err error + r := receivers.GetOrAdd( + oCfg, + func() (lh component.Component) { + lh, err = newLibhoneyReceiver(oCfg, &set) + return lh + }, + ) + if err != nil { + return nil, err + } + + r.Unwrap().(*libhoneyReceiver).registerTraceConsumer(nextConsumer) + return r, nil +} + +var receivers = sharedcomponent.NewSharedComponents() diff --git a/receiver/libhoneyreceiver/generated_component_test.go b/receiver/libhoneyreceiver/generated_component_test.go new file mode 100644 index 000000000000..f220dbec36b7 --- /dev/null +++ b/receiver/libhoneyreceiver/generated_component_test.go @@ -0,0 +1,76 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package libhoneyreceiver + +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, "libhoney", 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: "logs", + createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateLogs(ctx, set, cfg, consumertest.NewNop()) + }, + }, + + { + name: "traces", + createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateTraces(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/libhoneyreceiver/generated_package_test.go b/receiver/libhoneyreceiver/generated_package_test.go new file mode 100644 index 000000000000..03bb3911aeb1 --- /dev/null +++ b/receiver/libhoneyreceiver/generated_package_test.go @@ -0,0 +1,13 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package libhoneyreceiver + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/receiver/libhoneyreceiver/go.mod b/receiver/libhoneyreceiver/go.mod new file mode 100644 index 000000000000..58ea39a586a9 --- /dev/null +++ b/receiver/libhoneyreceiver/go.mod @@ -0,0 +1,76 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver + +go 1.22.0 + +require ( + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/collector/component/componenttest v0.115.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/config/confighttp v0.115.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/confmap v1.21.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/consumer v1.21.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/consumer/consumertest v0.115.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/receiver/receivertest v0.115.1-0.20241206185113-3f3e208e71b8 + go.uber.org/goleak v1.3.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.8.0 // 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.2.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.11 // 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.2 // 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/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent v0.115.0 + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/cors v1.11.1 // indirect + go.opentelemetry.io/collector/client v1.21.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/component/componentstatus v0.115.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/config/configauth v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/config/configcompression v1.21.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/config/configopaque v1.21.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/config/configtls v1.21.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/config/internal v0.115.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.115.0 // indirect + go.opentelemetry.io/collector/consumer/consumerprofiles v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/extension v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/extension/auth v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/pdata v1.21.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/pipeline v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/receiver v0.115.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/receiver/receiverprofiles v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent => ../../internal/sharedcomponent diff --git a/receiver/libhoneyreceiver/go.sum b/receiver/libhoneyreceiver/go.sum new file mode 100644 index 000000000000..5cb28e5c29a3 --- /dev/null +++ b/receiver/libhoneyreceiver/go.sum @@ -0,0 +1,174 @@ +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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +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.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/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/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +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.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ= +github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo= +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/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +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/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.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/client v1.21.1-0.20241206185113-3f3e208e71b8 h1:VfdixIcglr5IZhu6ogj8/uEMnf9Oi798V0td47/9jHg= +go.opentelemetry.io/collector/client v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:jYJGiL0UA975OOyHmjbQSokNWt1OiviI5KjPOMUMGwc= +go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8 h1:PtCINrFFDFi6aJRv8toOvLoKzu4qtz389PVcFlP7ydE= +go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:oIUFiH7w1eOimdeYhFI+gAIxYSiLDocKVJ0PTvX7d6s= +go.opentelemetry.io/collector/component/componentstatus v0.115.1-0.20241206185113-3f3e208e71b8 h1:DD5ahJ8YNJ32hxGU4RSw5Y3EVuZg/IZr5YtykvkfSo8= +go.opentelemetry.io/collector/component/componentstatus v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:36A+9XSiOz0Cdhq+UwwPRlEr5CYuSkEnVO9om4BH7d0= +go.opentelemetry.io/collector/component/componenttest v0.115.1-0.20241206185113-3f3e208e71b8 h1:Bic9twYk1GtkTNvzlt9rPCJEavRc5QYdSTN6Ug3hi9Q= +go.opentelemetry.io/collector/component/componenttest v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:PzXvNqKLCiSADZGZFKH+IOHMkaQ0GTHuzysfVbTPKYY= +go.opentelemetry.io/collector/config/configauth v0.115.1-0.20241206185113-3f3e208e71b8 h1:ih4kf5/HN019ZFPGUWR31+3oRaUa3khBEL8jvY8AZrs= +go.opentelemetry.io/collector/config/configauth v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:C7anpb3Rf4KswMT+dgOzkW9UX0z/65PLORpUw3p0VYc= +go.opentelemetry.io/collector/config/configcompression v1.21.1-0.20241206185113-3f3e208e71b8 h1:DnwOMnt/KvdDMwl8jur0e0E0RZ/H2TRHvXSiEmZCosM= +go.opentelemetry.io/collector/config/configcompression v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:LvYG00tbPTv0NOLoZN0wXq1F5thcxvukO8INq7xyfWU= +go.opentelemetry.io/collector/config/confighttp v0.115.1-0.20241206185113-3f3e208e71b8 h1:A8I7RHjEAnRBbEjAjtHg0fwWIb9yxDnk7s5tSNZICDE= +go.opentelemetry.io/collector/config/confighttp v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:Wr50ut12NmCEAl4bWLJryw2EjUmJTtYRg89560Q51wc= +go.opentelemetry.io/collector/config/configopaque v1.21.1-0.20241206185113-3f3e208e71b8 h1:y6nKsNrgQhMQW2naoyz4sMkOitAXytjhwx3lglhp+vg= +go.opentelemetry.io/collector/config/configopaque v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:sW0t0iI/VfRL9VYX7Ik6XzVgPcR+Y5kejTLsYcMyDWs= +go.opentelemetry.io/collector/config/configtelemetry v0.115.1-0.20241206185113-3f3e208e71b8 h1:b+0cqGeO0ZdILW5lsTzX29llVu1Me/Bxv0ya6iwOxcc= +go.opentelemetry.io/collector/config/configtelemetry v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:SlBEwQg0qly75rXZ6W1Ig8jN25KBVBkFIIAUI1GiAAE= +go.opentelemetry.io/collector/config/configtls v1.21.1-0.20241206185113-3f3e208e71b8 h1:4H2199VHuPO0D9/9RlsDnRRimxRwEFcUTmfT14qv26E= +go.opentelemetry.io/collector/config/configtls v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:5EsNefPfVCMOTlOrr3wyj7LrsOgY7V8iqRl8oFZEqtw= +go.opentelemetry.io/collector/config/internal v0.115.0 h1:eVk57iufZpUXyPJFKTb1Ebx5tmcCyroIlt427r5pxS8= +go.opentelemetry.io/collector/config/internal v0.115.0/go.mod h1:OVkadRWlKAoWjHslqjWtBLAne8ceQm8WYT71ZcBWLFc= +go.opentelemetry.io/collector/confmap v1.21.1-0.20241206185113-3f3e208e71b8 h1:CNLAB32cTRsaRJCnb+1T9y6XxJzmDtEbo2svat6/b4g= +go.opentelemetry.io/collector/confmap v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:Rrhs+MWoaP6AswZp+ReQ2VO9dfOfcUjdjiSHBsG+nec= +go.opentelemetry.io/collector/consumer v1.21.1-0.20241206185113-3f3e208e71b8 h1:GYE8iqLaknLjnrOM8QP+PBi7FpJGzCktMg1A9kgBbWg= +go.opentelemetry.io/collector/consumer v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:FQcC4ThMtRYY41dv+IPNK8POLLhAFY3r1YR5fuP7iiY= +go.opentelemetry.io/collector/consumer/consumererror v0.115.0 h1:yli//xBCQMPZKXNgNlXemo4dvqhnFrAmCZ11DvQgmcY= +go.opentelemetry.io/collector/consumer/consumererror v0.115.0/go.mod h1:LwVzAvQ6ZVNG7mbOvurbAo+W/rKws0IcjOwriuZXqPE= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.115.1-0.20241206185113-3f3e208e71b8 h1:ysXU7y4ltc7p1h3gQFtA7Cr3Qxn/10An8adNYPOeVUQ= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:IzEmZ91Tp7TBxVDq8Cc9xvLsmO7H08njr6Pu9P5d9ns= +go.opentelemetry.io/collector/consumer/consumertest v0.115.1-0.20241206185113-3f3e208e71b8 h1:zinrZujQGjMJhWo926FIwcIy4nMgwoYXnMe99nn0xDQ= +go.opentelemetry.io/collector/consumer/consumertest v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:ybjALRJWR6aKNOzEMy1T1ruCULVDEjj4omtOJMrH/kU= +go.opentelemetry.io/collector/extension v0.115.1-0.20241206185113-3f3e208e71b8 h1:4LCz2FyEYJk7yHoSWcQZbx6MPC2aXeDelTY8D9eoBOw= +go.opentelemetry.io/collector/extension v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:HI7Ak6loyi6ZrZPsQJW1OO1wbaAW8OqXLFNQlTZnreQ= +go.opentelemetry.io/collector/extension/auth v0.115.1-0.20241206185113-3f3e208e71b8 h1:rIgjShZE8h5MlOmuTmcz+obCOTSUXMkg56nWj/maXy4= +go.opentelemetry.io/collector/extension/auth v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:3w+2mzeb2OYNOO4Bi41TUo4jr32ap2y7AOq64IDpxQo= +go.opentelemetry.io/collector/extension/auth/authtest v0.115.0 h1:OZe7dKbZ01qodSpZU0ZYzI6zpmmzJ3UvfdBSFAbSgDw= +go.opentelemetry.io/collector/extension/auth/authtest v0.115.0/go.mod h1:fk9WCXP0x91Q64Z8HZKWTHh9PWtgoWE1KXe3n2Bff3U= +go.opentelemetry.io/collector/pdata v1.21.1-0.20241206185113-3f3e208e71b8 h1:PUaCJ1XIIomqXvFBF6hMFikhZIwoBc57UP7xlaRT//I= +go.opentelemetry.io/collector/pdata v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:GKb1/zocKJMvxKbS+sl0W85lxhYBTFJ6h6I1tphVyDU= +go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8 h1:+RGyM6ZhtNHRaiNbIiJ82Ik6TFmk3BCOgLvhw509Hc4= +go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:jGzdNfO0XTtfLjXCL/uCC1livg1LlfR+ix2WE/z3RpQ= +go.opentelemetry.io/collector/pdata/testdata v0.115.0 h1:Rblz+AKXdo3fG626jS+KSd0OSA4uMXcTQfpwed6P8LI= +go.opentelemetry.io/collector/pdata/testdata v0.115.0/go.mod h1:inNnRt6S2Nn260EfCBEcjesjlKOSsr0jPwkPqpBkt4s= +go.opentelemetry.io/collector/pipeline v0.115.1-0.20241206185113-3f3e208e71b8 h1:In55kZRXRq1whMsZVeAIyaZ/enb+m673PxzBwrsQZm0= +go.opentelemetry.io/collector/pipeline v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:qE3DmoB05AW0C3lmPvdxZqd/H4po84NPzd5MrqgtL74= +go.opentelemetry.io/collector/receiver v0.115.1-0.20241206185113-3f3e208e71b8 h1:8AL/vaRXeGL6rw7E+RZJEomG/xs2/X9NxhS9RcqKsNU= +go.opentelemetry.io/collector/receiver v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:nBSCh2O/WUcfgpJ+Jpz+B0z0Hn5jHeRvF2WmLij5EIY= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.115.1-0.20241206185113-3f3e208e71b8 h1:Nq/nLqbMLMKHN3tv/doV+BhS529q800HFE85o0r/XcI= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:05E5hGujWeeXJmzKZwTdHyZ/+rRyrQlQB5p5Q2XY39M= +go.opentelemetry.io/collector/receiver/receivertest v0.115.1-0.20241206185113-3f3e208e71b8 h1:cOsmTAvpuiDHh5ggc/JnsF3nBFC9dQaswFvTDpujJqs= +go.opentelemetry.io/collector/receiver/receivertest v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:Y8Z9U/bz9Xpyt8GI8DxZZgryw3mnnIw+AeKVLTD2cP8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +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.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +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-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +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.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/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/libhoneyreceiver/internal/metadata/generated_status.go b/receiver/libhoneyreceiver/internal/metadata/generated_status.go new file mode 100644 index 000000000000..ef6ead1f4954 --- /dev/null +++ b/receiver/libhoneyreceiver/internal/metadata/generated_status.go @@ -0,0 +1,17 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("libhoney") + ScopeName = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver" +) + +const ( + TracesStability = component.StabilityLevelDevelopment + LogsStability = component.StabilityLevelDevelopment +) diff --git a/receiver/libhoneyreceiver/libhoney.go b/receiver/libhoneyreceiver/libhoney.go new file mode 100644 index 000000000000..4ad1faab8fbb --- /dev/null +++ b/receiver/libhoneyreceiver/libhoney.go @@ -0,0 +1,127 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package libhoneyreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver" + +import ( + "context" + "errors" + "net" + "net/http" + "sync" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/receiverhelper" + "go.uber.org/zap" +) + +type libhoneyReceiver struct { + cfg *Config + serverHTTP *http.Server + + nextTraces consumer.Traces + nextLogs consumer.Logs + shutdownWG sync.WaitGroup + + obsrepHTTP *receiverhelper.ObsReport + + settings *receiver.Settings +} + +type TeamInfo struct { + Slug string `json:"slug"` +} + +type EnvironmentInfo struct { + Slug string `json:"slug"` + Name string `json:"name"` +} + +type AuthInfo struct { + APIKeyAccess map[string]bool `json:"api_key_access"` + Team TeamInfo `json:"team"` + Environment EnvironmentInfo `json:"environment"` +} + +func newLibhoneyReceiver(cfg *Config, set *receiver.Settings) (*libhoneyReceiver, error) { + r := &libhoneyReceiver{ + cfg: cfg, + nextTraces: nil, + settings: set, + } + + var err error + r.obsrepHTTP, err = receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ + ReceiverID: set.ID, + Transport: "http", + ReceiverCreateSettings: *set, + }) + if err != nil { + return nil, err + } + + return r, nil +} + +func (r *libhoneyReceiver) startHTTPServer(ctx context.Context, host component.Host) error { + // If HTTP is not enabled, nothing to start. + if r.cfg.HTTP == nil { + return nil + } + + if r.nextTraces != nil { + // initialize routes + r.settings.Logger.Debug("r.nextTraces found and ready to go") + } else { + r.settings.Logger.Debug("r.nextTraces is nil for some reason") + } + + // start server + var err error + r.settings.Logger.Info("Starting HTTP server", zap.String("endpoint", r.cfg.HTTP.ServerConfig.Endpoint)) + var hln net.Listener + if hln, err = r.cfg.HTTP.ServerConfig.ToListener(ctx); err != nil { + return err + } + + r.shutdownWG.Add(1) + go func() { + defer r.shutdownWG.Done() + + if errHTTP := r.serverHTTP.Serve(hln); errHTTP != nil && !errors.Is(errHTTP, http.ErrServerClosed) { + componentstatus.ReportStatus(host, componentstatus.NewFatalErrorEvent(errHTTP)) + } + }() + return nil +} + +func (r *libhoneyReceiver) Start(ctx context.Context, host component.Host) error { + if err := r.startHTTPServer(ctx, host); err != nil { + return errors.Join(err, r.Shutdown(ctx)) + } + + return nil +} + +// Shutdown is a method to turn off receiving. +func (r *libhoneyReceiver) Shutdown(ctx context.Context) error { + var err error + + if r.serverHTTP != nil { + err = r.serverHTTP.Shutdown(ctx) + } + + r.shutdownWG.Wait() + return err +} + +func (r *libhoneyReceiver) registerTraceConsumer(tc consumer.Traces) { + r.nextTraces = tc +} + +func (r *libhoneyReceiver) registerLogConsumer(tc consumer.Logs) { + r.nextLogs = tc +} diff --git a/receiver/libhoneyreceiver/metadata.yaml b/receiver/libhoneyreceiver/metadata.yaml new file mode 100644 index 000000000000..18710f4b2a5e --- /dev/null +++ b/receiver/libhoneyreceiver/metadata.yaml @@ -0,0 +1,8 @@ +type: libhoney + +status: + class: receiver + stability: + development: [traces, logs] + codeowners: + active: [TylerHelmuth] \ No newline at end of file diff --git a/versions.yaml b/versions.yaml index 243ca7358e6a..2603a69b8fdd 100644 --- a/versions.yaml +++ b/versions.yaml @@ -238,6 +238,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkametricsreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver + - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/lokireceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/memcachedreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/mongodbatlasreceiver From 964a652c4cce9f24361d05301351112e079297f8 Mon Sep 17 00:00:00 2001 From: Matt H Date: Thu, 12 Dec 2024 01:09:38 -0800 Subject: [PATCH 8/9] [exporter/loadbalancing] Add return_hostnames option to k8s resolver (#35411) **Description:** Adds an optional configuration option to the k8s resolver which allows for hostnames to be returned instead of IPs. This allows certain scenarios like using istio in sidecar mode. Requirements have been added to the documentation. **Link to tracking Issue:** #18412 **Testing:** Added corresponding hostname-based tests for adding backends/endpoints as well as deleting them. There were also tests missing for the k8s handler and so some tests were added as well there. Specifically failing if you want hostnames, but endpoints are returned that do not have hostnames. Aside from unit tests, also ran this in our lab cluster and deleted pods or performed rollouts to our statefulset. Somewhat tangential to the PR itself, but istio now reports mTLS traffic with zero workarounds required which was the motivation for the issue. **Documentation:** Added documentation explaining the new option and the requirements needed to use it. Also added an additional "important" note object specifically calling out that the k8s resolver needs RBAC to work. --- ...xporter-return-hostnames-k8s-resolver.yaml | 27 +++++ exporter/loadbalancingexporter/README.md | 3 + exporter/loadbalancingexporter/config.go | 7 +- .../loadbalancingexporter/loadbalancer.go | 1 + .../loadbalancingexporter/resolver_k8s.go | 24 ++-- .../resolver_k8s_handler.go | 57 ++++++++-- .../resolver_k8s_handler_test.go | 105 ++++++++++++++++++ .../resolver_k8s_test.go | 68 ++++++++++-- 8 files changed, 259 insertions(+), 33 deletions(-) create mode 100644 .chloggen/lbexporter-return-hostnames-k8s-resolver.yaml create mode 100644 exporter/loadbalancingexporter/resolver_k8s_handler_test.go diff --git a/.chloggen/lbexporter-return-hostnames-k8s-resolver.yaml b/.chloggen/lbexporter-return-hostnames-k8s-resolver.yaml new file mode 100644 index 000000000000..c8bd302960f2 --- /dev/null +++ b/.chloggen/lbexporter-return-hostnames-k8s-resolver.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: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: loadbalancingexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Adds a an optional configuration to the k8s resolver which returns hostnames instead of IPs for headless services pointing at statefulsets + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [18412] + +# (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: [] diff --git a/exporter/loadbalancingexporter/README.md b/exporter/loadbalancingexporter/README.md index 5df7812fb204..a6d5e8606d8e 100644 --- a/exporter/loadbalancingexporter/README.md +++ b/exporter/loadbalancingexporter/README.md @@ -96,6 +96,7 @@ Refer to [config.yaml](./testdata/config.yaml) for detailed examples on using th * `service` Kubernetes service to resolve, e.g. `lb-svc.lb-ns`. If no namespace is specified, an attempt will be made to infer the namespace for this collector, and if this fails it will fall back to the `default` namespace. * `ports` port to be used for exporting the traces to the addresses resolved from `service`. If `ports` is not specified, the default port 4317 is used. When multiple ports are specified, two backends are added to the load balancer as if they were at different pods. * `timeout` resolver timeout in go-Duration format, e.g. `5s`, `1d`, `30m`. If not specified, `1s` will be used. + * `return_hostnames` will return hostnames instead of IPs. This is useful in certain situations like using istio in sidecar mode. To use this feature, the `service` must be a headless `Service`, pointing at a `StatefulSet`, and the `service` must be what is specified under `.spec.serviceName` in the `StatefulSet`. * The `aws_cloud_map` node accepts the following properties: * `namespace` The CloudMap namespace where the service is register, e.g. `cloudmap`. If no `namespace` is specified, this will fail to start the Load Balancer exporter. * `service_name` The name of the service that you specified when you registered the instance, e.g. `otelcollectors`. If no `service_name` is specified, this will fail to start the Load Balancer exporter. @@ -231,6 +232,8 @@ service: ``` Kubernetes resolver example (For a more specific example: [example/k8s-resolver](./example/k8s-resolver/README.md)) +> [!IMPORTANT] +> The k8s resolver requires proper permissions. See [the full example](./example/k8s-resolver/README.md) for more information. ```yaml receivers: diff --git a/exporter/loadbalancingexporter/config.go b/exporter/loadbalancingexporter/config.go index b9682df16892..cee37b4d14fe 100644 --- a/exporter/loadbalancingexporter/config.go +++ b/exporter/loadbalancingexporter/config.go @@ -69,9 +69,10 @@ type DNSResolver struct { // K8sSvcResolver defines the configuration for the DNS resolver type K8sSvcResolver struct { - Service string `mapstructure:"service"` - Ports []int32 `mapstructure:"ports"` - Timeout time.Duration `mapstructure:"timeout"` + Service string `mapstructure:"service"` + Ports []int32 `mapstructure:"ports"` + Timeout time.Duration `mapstructure:"timeout"` + ReturnHostnames bool `mapstructure:"return_hostnames"` } type AWSCloudMapResolver struct { diff --git a/exporter/loadbalancingexporter/loadbalancer.go b/exporter/loadbalancingexporter/loadbalancer.go index 12ef0b5025fa..c14ad3d91183 100644 --- a/exporter/loadbalancingexporter/loadbalancer.go +++ b/exporter/loadbalancingexporter/loadbalancer.go @@ -102,6 +102,7 @@ func newLoadBalancer(logger *zap.Logger, cfg component.Config, factory component oCfg.Resolver.K8sSvc.Service, oCfg.Resolver.K8sSvc.Ports, oCfg.Resolver.K8sSvc.Timeout, + oCfg.Resolver.K8sSvc.ReturnHostnames, telemetry, ) if err != nil { diff --git a/exporter/loadbalancingexporter/resolver_k8s.go b/exporter/loadbalancingexporter/resolver_k8s.go index 87940260a880..040eb8120814 100644 --- a/exporter/loadbalancingexporter/resolver_k8s.go +++ b/exporter/loadbalancingexporter/resolver_k8s.go @@ -61,6 +61,7 @@ type k8sResolver struct { endpoints []string onChangeCallbacks []func([]string) + returnNames bool stopCh chan struct{} updateLock sync.RWMutex @@ -75,6 +76,7 @@ func newK8sResolver(clt kubernetes.Interface, service string, ports []int32, timeout time.Duration, + returnNames bool, tb *metadata.TelemetryBuilder, ) (*k8sResolver, error) { if len(service) == 0 { @@ -115,9 +117,10 @@ func newK8sResolver(clt kubernetes.Interface, epsStore := &sync.Map{} h := &handler{ - endpoints: epsStore, - logger: logger, - telemetry: tb, + endpoints: epsStore, + logger: logger, + telemetry: tb, + returnNames: returnNames, } r := &k8sResolver{ logger: logger, @@ -131,6 +134,7 @@ func newK8sResolver(clt kubernetes.Interface, stopCh: make(chan struct{}), lwTimeout: timeout, telemetry: tb, + returnNames: returnNames, } h.callback = r.resolve @@ -187,13 +191,19 @@ func (r *k8sResolver) resolve(ctx context.Context) ([]string, error) { defer r.shutdownWg.Done() var backends []string - r.endpointsStore.Range(func(address, _ any) bool { - addr := address.(string) + var ep string + r.endpointsStore.Range(func(host, _ any) bool { + switch r.returnNames { + case true: + ep = fmt.Sprintf("%s.%s.%s", host, r.svcName, r.svcNs) + default: + ep = host.(string) + } if len(r.port) == 0 { - backends = append(backends, addr) + backends = append(backends, ep) } else { for _, port := range r.port { - backends = append(backends, net.JoinHostPort(addr, strconv.FormatInt(int64(port), 10))) + backends = append(backends, net.JoinHostPort(ep, strconv.FormatInt(int64(port), 10))) } } return true diff --git a/exporter/loadbalancingexporter/resolver_k8s_handler.go b/exporter/loadbalancingexporter/resolver_k8s_handler.go index 186111eba4d5..895c7fbfca2c 100644 --- a/exporter/loadbalancingexporter/resolver_k8s_handler.go +++ b/exporter/loadbalancingexporter/resolver_k8s_handler.go @@ -17,19 +17,31 @@ import ( var _ cache.ResourceEventHandler = (*handler)(nil) +const ( + epMissingHostnamesMsg = "Endpoints object missing hostnames" +) + type handler struct { - endpoints *sync.Map - callback func(ctx context.Context) ([]string, error) - logger *zap.Logger - telemetry *metadata.TelemetryBuilder + endpoints *sync.Map + callback func(ctx context.Context) ([]string, error) + logger *zap.Logger + telemetry *metadata.TelemetryBuilder + returnNames bool } func (h handler) OnAdd(obj any, _ bool) { var endpoints map[string]bool + var ok bool switch object := obj.(type) { case *corev1.Endpoints: - endpoints = convertToEndpoints(object) + ok, endpoints = convertToEndpoints(h.returnNames, object) + if !ok { + h.logger.Warn(epMissingHostnamesMsg, zap.Any("obj", obj)) + h.telemetry.LoadbalancerNumResolutions.Add(context.Background(), 1, metric.WithAttributeSet(k8sResolverFailureAttrSet)) + return + } + default: // unsupported h.logger.Warn("Got an unexpected Kubernetes data type during the inclusion of a new pods for the service", zap.Any("obj", obj)) h.telemetry.LoadbalancerNumResolutions.Add(context.Background(), 1, metric.WithAttributeSet(k8sResolverFailureAttrSet)) @@ -56,8 +68,14 @@ func (h handler) OnUpdate(oldObj, newObj any) { return } - oldEndpoints := convertToEndpoints(oldEps) - newEndpoints := convertToEndpoints(newEps) + _, oldEndpoints := convertToEndpoints(h.returnNames, oldEps) + hostnameOk, newEndpoints := convertToEndpoints(h.returnNames, newEps) + if !hostnameOk { + h.logger.Warn(epMissingHostnamesMsg, zap.Any("obj", newEps)) + h.telemetry.LoadbalancerNumResolutions.Add(context.Background(), 1, metric.WithAttributeSet(k8sResolverFailureAttrSet)) + return + } + changed := false // Iterate through old endpoints and remove those that are not in the new list. @@ -80,6 +98,7 @@ func (h handler) OnUpdate(oldObj, newObj any) { } else { h.logger.Debug("No changes detected in the endpoints for the service", zap.Any("old", oldEps), zap.Any("new", newEps)) } + default: // unsupported h.logger.Warn("Got an unexpected Kubernetes data type during the update of the pods for a service", zap.Any("obj", oldObj)) h.telemetry.LoadbalancerNumResolutions.Add(context.Background(), 1, metric.WithAttributeSet(k8sResolverFailureAttrSet)) @@ -89,13 +108,20 @@ func (h handler) OnUpdate(oldObj, newObj any) { func (h handler) OnDelete(obj any) { var endpoints map[string]bool + var ok bool + switch object := obj.(type) { case *cache.DeletedFinalStateUnknown: h.OnDelete(object.Obj) return case *corev1.Endpoints: if object != nil { - endpoints = convertToEndpoints(object) + ok, endpoints = convertToEndpoints(h.returnNames, object) + if !ok { + h.logger.Warn(epMissingHostnamesMsg, zap.Any("obj", obj)) + h.telemetry.LoadbalancerNumResolutions.Add(context.Background(), 1, metric.WithAttributeSet(k8sResolverFailureAttrSet)) + return + } } default: // unsupported h.logger.Warn("Got an unexpected Kubernetes data type during the removal of the pods for a service", zap.Any("obj", obj)) @@ -110,14 +136,21 @@ func (h handler) OnDelete(obj any) { } } -func convertToEndpoints(eps ...*corev1.Endpoints) map[string]bool { - ipAddress := map[string]bool{} +func convertToEndpoints(retNames bool, eps ...*corev1.Endpoints) (bool, map[string]bool) { + res := map[string]bool{} for _, ep := range eps { for _, subsets := range ep.Subsets { for _, addr := range subsets.Addresses { - ipAddress[addr.IP] = true + if retNames { + if addr.Hostname == "" { + return false, nil + } + res[addr.Hostname] = true + } else { + res[addr.IP] = true + } } } } - return ipAddress + return true, res } diff --git a/exporter/loadbalancingexporter/resolver_k8s_handler_test.go b/exporter/loadbalancingexporter/resolver_k8s_handler_test.go new file mode 100644 index 000000000000..1a8cbd70c223 --- /dev/null +++ b/exporter/loadbalancingexporter/resolver_k8s_handler_test.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package loadbalancingexporter + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestConvertToEndpoints(tst *testing.T) { + // Create dummy Endpoints objects + endpoints1 := &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-endpoints-1", + Namespace: "test-namespace", + }, + Subsets: []corev1.EndpointSubset{ + { + Addresses: []corev1.EndpointAddress{ + { + Hostname: "pod-1", + IP: "192.168.10.101", + }, + }, + }, + }, + } + endpoints2 := &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-endpoints-2", + Namespace: "test-namespace", + }, + Subsets: []corev1.EndpointSubset{ + { + Addresses: []corev1.EndpointAddress{ + { + Hostname: "pod-2", + IP: "192.168.10.102", + }, + }, + }, + }, + } + endpoints3 := &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-endpoints-3", + Namespace: "test-namespace", + }, + Subsets: []corev1.EndpointSubset{ + { + Addresses: []corev1.EndpointAddress{ + { + IP: "192.168.10.103", + }, + }, + }, + }, + } + + tests := []struct { + name string + returnNames bool + includedEndpoints []*corev1.Endpoints + expectedEndpoints map[string]bool + wantNil bool + }{ + { + name: "return hostnames", + returnNames: true, + includedEndpoints: []*corev1.Endpoints{endpoints1, endpoints2}, + expectedEndpoints: map[string]bool{"pod-1": true, "pod-2": true}, + wantNil: false, + }, + { + name: "return IPs", + returnNames: false, + includedEndpoints: []*corev1.Endpoints{endpoints1, endpoints2, endpoints3}, + expectedEndpoints: map[string]bool{"192.168.10.101": true, "192.168.10.102": true, "192.168.10.103": true}, + wantNil: false, + }, + { + name: "missing hostname", + returnNames: true, + includedEndpoints: []*corev1.Endpoints{endpoints1, endpoints3}, + expectedEndpoints: nil, + wantNil: true, + }, + } + + for _, tt := range tests { + tst.Run(tt.name, func(tst *testing.T) { + ok, res := convertToEndpoints(tt.returnNames, tt.includedEndpoints...) + if tt.wantNil { + assert.Nil(tst, res) + } else { + assert.Equal(tst, tt.expectedEndpoints, res) + } + assert.Equal(tst, !tt.wantNil, ok) + }) + } +} diff --git a/exporter/loadbalancingexporter/resolver_k8s_test.go b/exporter/loadbalancingexporter/resolver_k8s_test.go index 5a4e77dd593b..c344fc1c6bb8 100644 --- a/exporter/loadbalancingexporter/resolver_k8s_test.go +++ b/exporter/loadbalancingexporter/resolver_k8s_test.go @@ -21,10 +21,11 @@ import ( func TestK8sResolve(t *testing.T) { type args struct { - logger *zap.Logger - service string - ports []int32 - namespace string + logger *zap.Logger + service string + ports []int32 + namespace string + returnHostnames bool } type suiteContext struct { endpoint *corev1.Endpoints @@ -32,7 +33,7 @@ func TestK8sResolve(t *testing.T) { resolver *k8sResolver } setupSuite := func(t *testing.T, args args) (*suiteContext, func(*testing.T)) { - service, defaultNs, ports := args.service, args.namespace, args.ports + service, defaultNs, ports, returnHostnames := args.service, args.namespace, args.ports, args.returnHostnames endpoint := &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: service, @@ -41,7 +42,10 @@ func TestK8sResolve(t *testing.T) { Subsets: []corev1.EndpointSubset{ { Addresses: []corev1.EndpointAddress{ - {IP: "192.168.10.100"}, + { + Hostname: "pod-0", + IP: "192.168.10.100", + }, }, }, }, @@ -50,14 +54,18 @@ func TestK8sResolve(t *testing.T) { for _, subset := range endpoint.Subsets { for _, address := range subset.Addresses { for _, port := range args.ports { - expectInit = append(expectInit, fmt.Sprintf("%s:%d", address.IP, port)) + if returnHostnames { + expectInit = append(expectInit, fmt.Sprintf("%s.%s.%s:%d", address.Hostname, service, defaultNs, port)) + } else { + expectInit = append(expectInit, fmt.Sprintf("%s:%d", address.IP, port)) + } } } } cl := fake.NewSimpleClientset(endpoint) _, tb := getTelemetryAssets(t) - res, err := newK8sResolver(cl, zap.NewNop(), service, ports, defaultListWatchTimeout, tb) + res, err := newK8sResolver(cl, zap.NewNop(), service, ports, defaultListWatchTimeout, returnHostnames, tb) require.NoError(t, err) require.NoError(t, res.start(context.Background())) @@ -81,7 +89,7 @@ func TestK8sResolve(t *testing.T) { verifyFn func(*suiteContext, args) error }{ { - name: "simulate append the backend ip address", + name: "add new IP to existing backends", args: args{ logger: zap.NewNop(), service: "lb", @@ -153,7 +161,45 @@ func TestK8sResolve(t *testing.T) { }, }, { - name: "simulate change the backend ip address", + name: "add new hostname to existing backends", + args: args{ + logger: zap.NewNop(), + service: "lb", + namespace: "default", + ports: []int32{8080, 9090}, + returnHostnames: true, + }, + simulateFn: func(suiteCtx *suiteContext, args args) error { + endpoint, exist := suiteCtx.endpoint.DeepCopy(), suiteCtx.endpoint.DeepCopy() + endpoint.Subsets = append(endpoint.Subsets, corev1.EndpointSubset{ + Addresses: []corev1.EndpointAddress{{IP: "10.10.0.11", Hostname: "pod-1"}}, + }) + patch := client.MergeFrom(exist) + data, err := patch.Data(endpoint) + if err != nil { + return err + } + _, err = suiteCtx.clientset.CoreV1().Endpoints(args.namespace). + Patch(context.TODO(), args.service, types.MergePatchType, data, metav1.PatchOptions{}) + return err + }, + verifyFn: func(ctx *suiteContext, _ args) error { + if _, err := ctx.resolver.resolve(context.Background()); err != nil { + return err + } + + assert.Equal(t, []string{ + "pod-0.lb.default:8080", + "pod-0.lb.default:9090", + "pod-1.lb.default:8080", + "pod-1.lb.default:9090", + }, ctx.resolver.Endpoints(), "resolver failed, endpoints not equal") + + return nil + }, + }, + { + name: "change existing backend ip address", args: args{ logger: zap.NewNop(), service: "lb", @@ -281,7 +327,7 @@ func Test_newK8sResolver(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, tb := getTelemetryAssets(t) - got, err := newK8sResolver(fake.NewSimpleClientset(), tt.args.logger, tt.args.service, tt.args.ports, defaultListWatchTimeout, tb) + got, err := newK8sResolver(fake.NewSimpleClientset(), tt.args.logger, tt.args.service, tt.args.ports, defaultListWatchTimeout, false, tb) if tt.wantErr != nil { require.ErrorIs(t, err, tt.wantErr) } else { From 783c00a1d0d6d40c8af19567ba63cbee6831b2ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:48:01 -0700 Subject: [PATCH 9/9] fix(deps): update module golang.org/x/crypto to v0.31.0 [security] (#36793) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | golang.org/x/crypto | `v0.29.0` -> `v0.31.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fcrypto/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/golang.org%2fx%2fcrypto/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/golang.org%2fx%2fcrypto/v0.29.0/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fcrypto/v0.29.0/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- > [!WARNING] > Some dependencies could not be looked up. Check the Dependency Dashboard for more information. ### GitHub Vulnerability Alerts #### [CVE-2024-45337](https://redirect.github.com/golang/crypto/commit/b4f1988a35dee11ec3e05d6bf3e90b695fbd8909) Applications and libraries which misuse the ServerConfig.PublicKeyCallback callback may be susceptible to an authorization bypass. The documentation for ServerConfig.PublicKeyCallback says that "A call to this function does not guarantee that the key offered is in fact used to authenticate." Specifically, the SSH protocol allows clients to inquire about whether a public key is acceptable before proving control of the corresponding private key. PublicKeyCallback may be called with multiple keys, and the order in which the keys were provided cannot be used to infer which key the client successfully authenticated with, if any. Some applications, which store the key(s) passed to PublicKeyCallback (or derived information) and make security relevant determinations based on it once the connection is established, may make incorrect assumptions. For example, an attacker may send public keys A and B, and then authenticate with A. PublicKeyCallback would be called only twice, first with A and then with B. A vulnerable application may then make authorization decisions based on key B for which the attacker does not actually control the private key. Since this API is widely misused, as a partial mitigation golang.org/x/cry...@​v0.31.0 enforces the property that, when successfully authenticating via public key, the last key passed to ServerConfig.PublicKeyCallback will be the key used to authenticate the connection. PublicKeyCallback will now be called multiple times with the same key, if necessary. Note that the client may still not control the last key passed to PublicKeyCallback if the connection is then authenticated with a different method, such as PasswordCallback, KeyboardInteractiveCallback, or NoClientAuth. Users should be using the Extensions field of the Permissions return value from the various authentication callbacks to record data associated with the authentication attempt instead of referencing external state. Once the connection is established the state corresponding to the successful authentication attempt can be retrieved via the ServerConn.Permissions field. Note that some third-party libraries misuse the Permissions type by sharing it across authentication attempts; users of third-party libraries should refer to the relevant projects for guidance. --- ### Configuration 📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. â™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/open-telemetry/opentelemetry-collector-contrib). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: opentelemetrybot <107717825+opentelemetrybot@users.noreply.github.com> --- receiver/podmanreceiver/go.mod | 6 +++--- receiver/podmanreceiver/go.sum | 16 ++++++++-------- receiver/sshcheckreceiver/go.mod | 6 +++--- receiver/sshcheckreceiver/go.sum | 16 ++++++++-------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/receiver/podmanreceiver/go.mod b/receiver/podmanreceiver/go.mod index e68791119ca4..5fb8fda845c6 100644 --- a/receiver/podmanreceiver/go.mod +++ b/receiver/podmanreceiver/go.mod @@ -20,7 +20,7 @@ require ( go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 ) require ( @@ -51,8 +51,8 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect go.opentelemetry.io/otel/trace v1.32.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.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.2 // indirect diff --git a/receiver/podmanreceiver/go.sum b/receiver/podmanreceiver/go.sum index f09832740bf0..3f401c1837b8 100644 --- a/receiver/podmanreceiver/go.sum +++ b/receiver/podmanreceiver/go.sum @@ -103,8 +103,8 @@ 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/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 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= @@ -119,14 +119,14 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ 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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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= diff --git a/receiver/sshcheckreceiver/go.mod b/receiver/sshcheckreceiver/go.mod index 15fd9c5c6c46..6f5224a271c0 100644 --- a/receiver/sshcheckreceiver/go.mod +++ b/receiver/sshcheckreceiver/go.mod @@ -22,7 +22,7 @@ require ( go.opentelemetry.io/collector/scraper v0.115.1-0.20241206185113-3f3e208e71b8 go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 ) require ( @@ -67,8 +67,8 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/zap v1.27.0 golang.org/x/net v0.29.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/receiver/sshcheckreceiver/go.sum b/receiver/sshcheckreceiver/go.sum index 11282c19970d..3351f9ec2a45 100644 --- a/receiver/sshcheckreceiver/go.sum +++ b/receiver/sshcheckreceiver/go.sum @@ -119,8 +119,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -150,23 +150,23 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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=