diff --git a/.chloggen/add-confmap-to-mdatagen.yaml b/.chloggen/add-confmap-to-mdatagen.yaml new file mode 100644 index 00000000000..bfc7b0d7634 --- /dev/null +++ b/.chloggen/add-confmap-to-mdatagen.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: mdatagen + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add `converter` and `provider` module classes + +# One or more tracking issues or pull requests related to the change +issues: [12467] + +# (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: + +# 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/.chloggen/configauth-omitempty.yaml b/.chloggen/configauth-omitempty.yaml new file mode 100644 index 00000000000..1aeaf7caa6d --- /dev/null +++ b/.chloggen/configauth-omitempty.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: configauth + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add the `omitempty` mapstructure tag to struct fields + +# One or more tracking issues or pull requests related to the change +issues: [12191] + +# (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: This results in unset fields not being rendered when marshaling. + +# 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: [api] diff --git a/.chloggen/finishsize.yaml b/.chloggen/finishsize.yaml new file mode 100644 index 00000000000..57501528d34 --- /dev/null +++ b/.chloggen/finishsize.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: exporterhelper + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Deprecate `min_size_items` and `max_size_items` in favor of `min_size` and `max_size`. + +# One or more tracking issues or pull requests related to the change +issues: [12486] + +# (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: + +# 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/.chloggen/finishsize_breaking.yaml b/.chloggen/finishsize_breaking.yaml new file mode 100644 index 00000000000..3d072a23cd9 --- /dev/null +++ b/.chloggen/finishsize_breaking.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: exporterhelper + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Update MergeSplit function signature to use the new SizeConfig + +# One or more tracking issues or pull requests related to the change +issues: [12486] + +# (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: + +# 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: [api] diff --git a/.chloggen/finishsize_deprecation.yaml b/.chloggen/finishsize_deprecation.yaml new file mode 100644 index 00000000000..c9678d28766 --- /dev/null +++ b/.chloggen/finishsize_deprecation.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: exporterhelper + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Deprecate MinSizeConfig and MaxSizeItems. + +# One or more tracking issues or pull requests related to the change +issues: [12486] + +# (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: + +# 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: [api] diff --git a/.chloggen/fix-wrong-error-log.yaml b/.chloggen/fix-wrong-error-log.yaml new file mode 100644 index 00000000000..144507be08b --- /dev/null +++ b/.chloggen/fix-wrong-error-log.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: exporterhelper + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Fix bug where the error logged when conversion of data fails is always nil + +# One or more tracking issues or pull requests related to the change +issues: [12510] + +# (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: + +# 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/.chloggen/iblancasa-memorylimiterprocessor-add-support-profiles.yaml b/.chloggen/iblancasa-memorylimiterprocessor-add-support-profiles.yaml new file mode 100644 index 00000000000..4f55e9c3d8d --- /dev/null +++ b/.chloggen/iblancasa-memorylimiterprocessor-add-support-profiles.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: memorylimiterprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add support for profiles. + +# One or more tracking issues or pull requests related to the change +issues: [12453] + +# (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: + +# 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/.chloggen/jackgopack4-add-converters-component-command.yaml b/.chloggen/jackgopack4-add-converters-component-command.yaml new file mode 100644 index 00000000000..0547fb4a8e9 --- /dev/null +++ b/.chloggen/jackgopack4-add-converters-component-command.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: otelcol + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Converters are now available in the `components` command. + +# One or more tracking issues or pull requests related to the change +issues: [11900, 12385] + +# (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: + +# 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, api] diff --git a/.chloggen/mx-psi_deprecate-func-methods.yaml b/.chloggen/mx-psi_deprecate-func-methods.yaml new file mode 100644 index 00000000000..90908efe992 --- /dev/null +++ b/.chloggen/mx-psi_deprecate-func-methods.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: extension/extensionauth + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Deprecate methods on `*Func` types. + +# One or more tracking issues or pull requests related to the change +issues: [12480] + +# (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: + +# 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: [api] diff --git a/.chloggen/mx-psi_error-map.yaml b/.chloggen/mx-psi_error-map.yaml new file mode 100644 index 00000000000..108721b2b19 --- /dev/null +++ b/.chloggen/mx-psi_error-map.yaml @@ -0,0 +1,26 @@ +# 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. otlpreceiver) +component: confmap + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Surface YAML parsing errors when they happen at the top-level. + +# One or more tracking issues or pull requests related to the change +issues: [12180] + +# (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: | + This adds context to some instances of the error "retrieved value (type=string) cannot be used as a Conf", which typically happens because of invalid YAML documents + +# 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/.chloggen/mx-psi_extension-1.0-bis.yaml b/.chloggen/mx-psi_extension-1.0-bis.yaml new file mode 100644 index 00000000000..5bbf0235250 --- /dev/null +++ b/.chloggen/mx-psi_extension-1.0-bis.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: extension + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Mark module as stable + +# One or more tracking issues or pull requests related to the change +issues: [11005] + +# (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: + +# 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: [api] diff --git a/.chloggen/mx-psi_extensionauth.yaml b/.chloggen/mx-psi_extensionauth.yaml new file mode 100644 index 00000000000..19cfd45721b --- /dev/null +++ b/.chloggen/mx-psi_extensionauth.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: extension/auth, extension/auth/authtest + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Deprecate extension/auth and the related test module in favor of extension/extensionauth + +# One or more tracking issues or pull requests related to the change +issues: [12478] + +# (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: + +# 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: [api] diff --git a/.chloggen/mx-psi_type-mismatch.yaml b/.chloggen/mx-psi_type-mismatch.yaml new file mode 100644 index 00000000000..117603187c2 --- /dev/null +++ b/.chloggen/mx-psi_type-mismatch.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: processor, connector, exporter, receiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Explicitly error out at component creation time if there is a type mismatch. + +# One or more tracking issues or pull requests related to the change +issues: [12305] + +# (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: + +# 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: [api] diff --git a/.chloggen/pprofile-sample-link-index.yaml b/.chloggen/pprofile-sample-link-index.yaml new file mode 100644 index 00000000000..01a62690547 --- /dev/null +++ b/.chloggen/pprofile-sample-link-index.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: pprofile + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add LinkIndex attribute to the generated Sample type + +# One or more tracking issues or pull requests related to the change +issues: [12485] + +# (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: + +# 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/.chloggen/service-omitempty.yaml b/.chloggen/service-omitempty.yaml new file mode 100644 index 00000000000..3c636376691 --- /dev/null +++ b/.chloggen/service-omitempty.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: service + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add the `omitempty` mapstructure tag to struct fields + +# One or more tracking issues or pull requests related to the change +issues: [12191] + +# (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: This results in unset fields not being rendered when marshaling. + +# 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: [api] diff --git a/.chloggen/update_metadata_schema.yaml b/.chloggen/update_metadata_schema.yaml new file mode 100644 index 00000000000..7b2e2ac5ca3 --- /dev/null +++ b/.chloggen/update_metadata_schema.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: mdatagen + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Update metadata schema with new fields without enforcing them + +# One or more tracking issues or pull requests related to the change +issues: [12359] + +# (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: + +# 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/.github/workflows/contrib-tests.yml b/.github/workflows/contrib-tests.yml index 9e4175f7968..8c3715e8327 100644 --- a/.github/workflows/contrib-tests.yml +++ b/.github/workflows/contrib-tests.yml @@ -36,7 +36,6 @@ jobs: - internal - pkg - cmd-0 - - cmd-1 - other steps: - name: Checkout Repo diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a05ae65a79..e5a292bac6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,14 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ - `cmd/mdatagen`: Remove `level` field from metrics definition (#12145) This mechanism will be added back once a new views mechanism is implemented. - `service`: Value for telemetry exporter `otlp.protocol` updated from `grpc/protobuf` to `grpc`. (#12337) +- `service`: internal metrics exported over Prometheus may differ from previous versions. (#11611) + + Users who do not customize the Prometheus reader should not be impacted. The change to update the internal telemetry to use [otel-go config](https://pkg.go.dev/go.opentelemetry.io/contrib/config) can cause unexpected behaviour + for end users. This change is caused by the default values in `config` being different from what the Collector has used in previous versions. The + following changes can occur when users configure their `service::telemetry::metrics::readers`: + - the metric name will append a `_total` suffix if `without_type_suffix` is not configured. Set `without_type_suffix` to `true` to disable this. + - units will be appended to metric name if `without_units` is not configured. Set `without_units` to `true` to disable this. + - a `target_info` metric will be emitted if `without_scope_info` is not configured. Set `without_scope_info` to `true` to disable this. ### 💡 Enhancements 💡 diff --git a/cmd/builder/internal/builder/main_test.go b/cmd/builder/internal/builder/main_test.go index e36e83ae745..b9d10b70779 100644 --- a/cmd/builder/internal/builder/main_test.go +++ b/cmd/builder/internal/builder/main_test.go @@ -76,8 +76,8 @@ var replaceModules = []string{ "/exporter/otlpexporter", "/exporter/otlphttpexporter", "/extension", - "/extension/auth", - "/extension/auth/authtest", + "/extension/extensionauth", + "/extension/extensionauth/extensionauthtest", "/extension/extensioncapabilities", "/extension/extensiontest", "/extension/zpagesextension", @@ -97,6 +97,7 @@ var replaceModules = []string{ "/processor/processortest", "/processor/batchprocessor", "/processor/memorylimiterprocessor", + "/processor/processorhelper/xprocessorhelper", "/processor/xprocessor", "/receiver", "/receiver/nopreceiver", diff --git a/cmd/builder/internal/builder/templates/main.go.tmpl b/cmd/builder/internal/builder/templates/main.go.tmpl index ff619f7d0fc..e3b1e8499f6 100644 --- a/cmd/builder/internal/builder/templates/main.go.tmpl +++ b/cmd/builder/internal/builder/templates/main.go.tmpl @@ -45,11 +45,17 @@ func main() { DefaultScheme: "{{ .ConfResolver.DefaultURIScheme }}", {{- end }} }, - }, ProviderModules: map[string]string{ + }, + ProviderModules: map[string]string{ {{- range .ConfmapProviders}} {{.Name}}.NewFactory().Create(confmap.ProviderSettings{}).Scheme(): "{{.GoMod}}", {{- end}} - }, + }, + ConverterModules: []string{ + {{- range .ConfmapConverters}} + "{{.GoMod}}", + {{- end}} + }, } if err := run(set); err != nil { diff --git a/cmd/mdatagen/internal/command.go b/cmd/mdatagen/internal/command.go index 9c5293f5fa1..7fe90ff22bd 100644 --- a/cmd/mdatagen/internal/command.go +++ b/cmd/mdatagen/internal/command.go @@ -13,6 +13,7 @@ import ( "path/filepath" "regexp" "runtime/debug" + "slices" "strings" "text/template" @@ -26,6 +27,13 @@ const ( statusEnd = "" ) +var nonComponents = []string{ + "cmd", + "converter", + "pkg", + "provider", +} + func getVersion() (string, error) { // the second returned value is a boolean, which is true if the binaries are built with module support. info, ok := debug.ReadBuildInfo() @@ -78,7 +86,7 @@ func run(ymlPath string) error { codeDir := filepath.Join(ymlDir, "internal", md.GeneratedPackageName) toGenerate := map[string]string{} if md.Status != nil { - if md.Status.Class != "cmd" && md.Status.Class != "pkg" { + if !slices.Contains(nonComponents, md.Status.Class) { toGenerate[filepath.Join(tmplDir, "status.go.tmpl")] = filepath.Join(codeDir, "generated_status.go") if err = generateFile(filepath.Join(tmplDir, "component_test.go.tmpl"), filepath.Join(ymlDir, "generated_component_test.go"), md, packageName); err != nil { diff --git a/cmd/mdatagen/internal/command_test.go b/cmd/mdatagen/internal/command_test.go index 058b7bff9cd..f846adbb006 100644 --- a/cmd/mdatagen/internal/command_test.go +++ b/cmd/mdatagen/internal/command_test.go @@ -330,6 +330,32 @@ Some info about a component componentClass: "extension", distros: []string{"contrib"}, }, + { + name: "readme with status for converter", + markdown: `# Some component + + + + +Some info about a component +`, + outputFile: "readme_with_status_converter.md", + componentClass: "converter", + distros: []string{"contrib"}, + }, + { + name: "readme with status for provider", + markdown: `# Some component + + + + +Some info about a component +`, + outputFile: "readme_with_status_provider.md", + componentClass: "provider", + distros: []string{"contrib"}, + }, { name: "readme with status with codeowners and seeking new", markdown: `# Some component diff --git a/cmd/mdatagen/internal/status.go b/cmd/mdatagen/internal/status.go index ebc95cff983..6f1f849d171 100644 --- a/cmd/mdatagen/internal/status.go +++ b/cmd/mdatagen/internal/status.go @@ -6,6 +6,7 @@ package internal // import "go.opentelemetry.io/collector/cmd/mdatagen/internal" import ( "errors" "fmt" + "slices" "sort" "go.opentelemetry.io/collector/component" @@ -40,12 +41,59 @@ type Codeowners struct { } type Status struct { - Stability StabilityMap `mapstructure:"stability"` - Distributions []string `mapstructure:"distributions"` - Class string `mapstructure:"class"` - Warnings []string `mapstructure:"warnings"` - Codeowners *Codeowners `mapstructure:"codeowners"` - UnsupportedPlatforms []string `mapstructure:"unsupported_platforms"` + Stability StabilityMap `mapstructure:"stability"` + Distributions []string `mapstructure:"distributions"` + Class string `mapstructure:"class"` + Warnings []string `mapstructure:"warnings"` + Codeowners *Codeowners `mapstructure:"codeowners"` + UnsupportedPlatforms []string `mapstructure:"unsupported_platforms"` + Deprecation DeprecationMap `mapstructure:"deprecation"` +} + +type DeprecationMap map[string]DeprecationInfo + +type DeprecationInfo struct { + Date string `mapstructure:"date"` + Migration string `mapstructure:"migration"` +} + +var validClasses = []string{ + "cmd", + "connector", + "converter", + "exporter", + "extension", + "pkg", + "processor", + "provider", + "receiver", + "scraper", +} + +var validStabilityKeys = []string{ + "converter", + "extension", + "logs", + "logs_to_traces", + "logs_to_metrics", + "logs_to_logs", + "logs_to_profiles", + "metrics", + "metrics_to_traces", + "metrics_to_metrics", + "metrics_to_logs", + "metrics_to_profiles", + "profiles", + "profiles_to_profiles", + "profiles_to_traces", + "profiles_to_metrics", + "profiles_to_logs", + "provider", + "traces_to_traces", + "traces_to_metrics", + "traces_to_logs", + "traces_to_profiles", + "traces", } func (s *Status) SortedDistributions() []string { @@ -87,10 +135,7 @@ func (s *Status) validateClass() error { if s.Class == "" { return errors.New("missing class") } - if s.Class != "receiver" && s.Class != "processor" && - s.Class != "exporter" && s.Class != "connector" && - s.Class != "extension" && s.Class != "scraper" && - s.Class != "cmd" && s.Class != "pkg" { + if !slices.Contains(validClasses, s.Class) { return fmt.Errorf("invalid class: %v", s.Class) } return nil @@ -108,27 +153,7 @@ func (ms StabilityMap) Validate() error { errs = errors.Join(errs, fmt.Errorf("missing component for stability: %v", stability)) } for _, c := range cmps { - if c != "metrics" && - c != "traces" && - c != "logs" && - c != "profiles" && - c != "traces_to_traces" && - c != "traces_to_metrics" && - c != "traces_to_logs" && - c != "traces_to_profiles" && - c != "metrics_to_traces" && - c != "metrics_to_metrics" && - c != "metrics_to_logs" && - c != "metrics_to_profiles" && - c != "logs_to_traces" && - c != "logs_to_metrics" && - c != "logs_to_logs" && - c != "logs_to_profiles" && - c != "profiles_to_profiles" && - c != "profiles_to_traces" && - c != "profiles_to_metrics" && - c != "profiles_to_logs" && - c != "extension" { + if !slices.Contains(validStabilityKeys, c) { errs = errors.Join(errs, fmt.Errorf("invalid component: %v", c)) } } diff --git a/cmd/mdatagen/internal/templates/readme.md.tmpl b/cmd/mdatagen/internal/templates/readme.md.tmpl index c99a9bde13a..7fe5ae902d8 100644 --- a/cmd/mdatagen/internal/templates/readme.md.tmpl +++ b/cmd/mdatagen/internal/templates/readme.md.tmpl @@ -7,7 +7,7 @@ {{- if ne $class "connector" }} {{- $idx := 0 }} {{- range $stability, $value := .Status.Stability }} -| {{ if not $idx }}Stability{{ else }} {{ end }} | [{{ toLowerCase $stability.String }}]{{ if ne $class "extension" }}: {{ stringsJoin $value ", " }} {{ end }} | +| {{ if not $idx }}Stability{{ else }} {{ end }} | [{{ toLowerCase $stability.String }}]{{ if and (ne $class "extension") (ne $class "converter") (ne $class "provider") }}: {{ stringsJoin $value ", " }} {{ end }} | {{- $idx = inc $idx }} {{- end }} {{- end}} diff --git a/cmd/mdatagen/internal/testdata/readme_with_status_converter.md b/cmd/mdatagen/internal/testdata/readme_with_status_converter.md new file mode 100644 index 00000000000..cb693070eaa --- /dev/null +++ b/cmd/mdatagen/internal/testdata/readme_with_status_converter.md @@ -0,0 +1,14 @@ +# Some component + + +| Status | | +| ------------- |-----------| +| Stability | [beta] | +| Distributions | [contrib] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aconverter%2Ffoo%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aconverter%2Ffoo) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aconverter%2Ffoo%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aconverter%2Ffoo) | + +[beta]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#beta +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib + + +Some info about a component diff --git a/cmd/mdatagen/internal/testdata/readme_with_status_provider.md b/cmd/mdatagen/internal/testdata/readme_with_status_provider.md new file mode 100644 index 00000000000..e4ab8542de4 --- /dev/null +++ b/cmd/mdatagen/internal/testdata/readme_with_status_provider.md @@ -0,0 +1,14 @@ +# Some component + + +| Status | | +| ------------- |-----------| +| Stability | [beta] | +| Distributions | [contrib] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aprovider%2Ffoo%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aprovider%2Ffoo) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aprovider%2Ffoo%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aprovider%2Ffoo) | + +[beta]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#beta +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib + + +Some info about a component diff --git a/cmd/mdatagen/metadata-schema.yaml b/cmd/mdatagen/metadata-schema.yaml index 375c5b0fbcf..aff35e5f9b4 100644 --- a/cmd/mdatagen/metadata-schema.yaml +++ b/cmd/mdatagen/metadata-schema.yaml @@ -13,15 +13,20 @@ generated_package_name: string # Required for components (Optional for subcomponents): A high-level view of the development status and use of this component status: # Required: The class of the component (For example receiver) - class: + class: # Required: The stability of the component - See https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stability-levels stability: - development: [] - alpha: [] - beta: [] - stable: [] - deprecated: [] - unmaintained: [] + development: [] + alpha: [] + beta: [] + stable: [] + deprecated: [] + unmaintained: [] + # [WIP] Will be a requirement for deprecated components: The deprecation information for the deprecated components + deprecation: + : + date: string + migration: string # Optional: The distributions that this component is bundled with (For example core or contrib). See statusdata.go for a list of common distros. distributions: [string] # Optional: A list of warnings that should be brought to the attention of users looking to use this component @@ -133,8 +138,6 @@ tests: # Optional: map of metric names with the key being the metric name and value # being described below. telemetry: - # Optional: level allows components to specify the minimum telemetry level for metrics to be produced. defaults to basic if not set. - level: string metrics: : # Required: whether the metric is collected by default. diff --git a/cmd/otelcorecol/builder-config.yaml b/cmd/otelcorecol/builder-config.yaml index 191da5a00ce..0004574d3e7 100644 --- a/cmd/otelcorecol/builder-config.yaml +++ b/cmd/otelcorecol/builder-config.yaml @@ -76,8 +76,8 @@ replaces: - go.opentelemetry.io/collector/exporter/otlpexporter => ../../exporter/otlpexporter - go.opentelemetry.io/collector/exporter/otlphttpexporter => ../../exporter/otlphttpexporter - go.opentelemetry.io/collector/extension => ../../extension - - go.opentelemetry.io/collector/extension/auth => ../../extension/auth - - go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest + - go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth + - go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest - go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities - go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest - go.opentelemetry.io/collector/extension/memorylimiterextension => ../../extension/memorylimiterextension @@ -99,6 +99,7 @@ replaces: - go.opentelemetry.io/collector/processor/batchprocessor => ../../processor/batchprocessor - go.opentelemetry.io/collector/processor/memorylimiterprocessor => ../../processor/memorylimiterprocessor - go.opentelemetry.io/collector/processor/xprocessor => ../../processor/xprocessor + - go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper => ../../processor/processorhelper/xprocessorhelper - go.opentelemetry.io/collector/receiver => ../../receiver - go.opentelemetry.io/collector/receiver/nopreceiver => ../../receiver/nopreceiver - go.opentelemetry.io/collector/receiver/otlpreceiver => ../../receiver/otlpreceiver diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index db96869d60f..2845f156c2e 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -104,7 +104,7 @@ require ( go.opentelemetry.io/collector/exporter/exporterhelper/xexporterhelper v0.120.0 // indirect go.opentelemetry.io/collector/exporter/exportertest v0.120.0 // indirect go.opentelemetry.io/collector/exporter/xexporter v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/extension/extensioncapabilities v0.120.0 // indirect go.opentelemetry.io/collector/extension/extensiontest v0.120.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.120.0 // indirect @@ -118,13 +118,14 @@ require ( go.opentelemetry.io/collector/pdata/testdata v0.120.0 // indirect go.opentelemetry.io/collector/pipeline v0.120.0 // indirect go.opentelemetry.io/collector/pipeline/xpipeline v0.120.0 // indirect + go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper v0.120.0 // indirect go.opentelemetry.io/collector/processor/processortest v0.120.0 // indirect go.opentelemetry.io/collector/processor/xprocessor v0.120.0 // indirect go.opentelemetry.io/collector/receiver/receivertest v0.120.0 // indirect go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 // indirect go.opentelemetry.io/collector/semconv v0.120.0 // indirect go.opentelemetry.io/collector/service v0.120.0 // indirect - go.opentelemetry.io/collector/service/hostcapabilities v0.120.0 // indirect + go.opentelemetry.io/collector/service/hostcapabilities v0.0.0-20250225223953-66e901c716a0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.9.0 // indirect go.opentelemetry.io/contrib/config v0.14.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect @@ -241,9 +242,9 @@ replace go.opentelemetry.io/collector/exporter/otlphttpexporter => ../../exporte replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities @@ -287,6 +288,8 @@ replace go.opentelemetry.io/collector/processor/memorylimiterprocessor => ../../ replace go.opentelemetry.io/collector/processor/xprocessor => ../../processor/xprocessor +replace go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper => ../../processor/processorhelper/xprocessorhelper + replace go.opentelemetry.io/collector/receiver => ../../receiver replace go.opentelemetry.io/collector/receiver/nopreceiver => ../../receiver/nopreceiver diff --git a/cmd/otelcorecol/main.go b/cmd/otelcorecol/main.go index d896f670c8b..e0257b0a2b2 100644 --- a/cmd/otelcorecol/main.go +++ b/cmd/otelcorecol/main.go @@ -36,13 +36,15 @@ func main() { yamlprovider.NewFactory(), }, }, - }, ProviderModules: map[string]string{ + }, + ProviderModules: map[string]string{ envprovider.NewFactory().Create(confmap.ProviderSettings{}).Scheme(): "go.opentelemetry.io/collector/confmap/provider/envprovider v1.26.0", fileprovider.NewFactory().Create(confmap.ProviderSettings{}).Scheme(): "go.opentelemetry.io/collector/confmap/provider/fileprovider v1.26.0", httpprovider.NewFactory().Create(confmap.ProviderSettings{}).Scheme(): "go.opentelemetry.io/collector/confmap/provider/httpprovider v1.26.0", httpsprovider.NewFactory().Create(confmap.ProviderSettings{}).Scheme(): "go.opentelemetry.io/collector/confmap/provider/httpsprovider v1.26.0", yamlprovider.NewFactory().Create(confmap.ProviderSettings{}).Scheme(): "go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.26.0", }, + ConverterModules: []string{}, } if err := run(set); err != nil { diff --git a/config/configauth/configauth.go b/config/configauth/configauth.go index 96c3e9c092b..d2e7f45ff0c 100644 --- a/config/configauth/configauth.go +++ b/config/configauth/configauth.go @@ -12,7 +12,7 @@ import ( "fmt" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/extension/auth" + "go.opentelemetry.io/collector/extension/extensionauth" ) var ( @@ -24,14 +24,14 @@ var ( // Authentication defines the auth settings for the receiver. type Authentication struct { // AuthenticatorID specifies the name of the extension to use in order to authenticate the incoming data point. - AuthenticatorID component.ID `mapstructure:"authenticator"` + AuthenticatorID component.ID `mapstructure:"authenticator,omitempty"` } -// GetServerAuthenticator attempts to select the appropriate auth.Server from the list of extensions, +// GetServerAuthenticator attempts to select the appropriate extensionauth.Server from the list of extensions, // based on the requested extension name. If an authenticator is not found, an error is returned. -func (a Authentication) GetServerAuthenticator(_ context.Context, extensions map[component.ID]component.Component) (auth.Server, error) { +func (a Authentication) GetServerAuthenticator(_ context.Context, extensions map[component.ID]component.Component) (extensionauth.Server, error) { if ext, found := extensions[a.AuthenticatorID]; found { - if server, ok := ext.(auth.Server); ok { + if server, ok := ext.(extensionauth.Server); ok { return server, nil } return nil, errNotServer @@ -40,12 +40,12 @@ func (a Authentication) GetServerAuthenticator(_ context.Context, extensions map return nil, fmt.Errorf("failed to resolve authenticator %q: %w", a.AuthenticatorID, errAuthenticatorNotFound) } -// GetClientAuthenticator attempts to select the appropriate auth.Client from the list of extensions, +// GetClientAuthenticator attempts to select the appropriate extensionauth.Client from the list of extensions, // based on the component id of the extension. If an authenticator is not found, an error is returned. // This should be only used by HTTP clients. -func (a Authentication) GetClientAuthenticator(_ context.Context, extensions map[component.ID]component.Component) (auth.Client, error) { +func (a Authentication) GetClientAuthenticator(_ context.Context, extensions map[component.ID]component.Component) (extensionauth.Client, error) { if ext, found := extensions[a.AuthenticatorID]; found { - if client, ok := ext.(auth.Client); ok { + if client, ok := ext.(extensionauth.Client); ok { return client, nil } return nil, errNotClient diff --git a/config/configauth/configauth_test.go b/config/configauth/configauth_test.go index 77cc1c8bb5d..f86d7d7abd7 100644 --- a/config/configauth/configauth_test.go +++ b/config/configauth/configauth_test.go @@ -12,11 +12,18 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/extension" - "go.opentelemetry.io/collector/extension/auth" + "go.opentelemetry.io/collector/extension/extensionauth" ) var mockID = component.MustNewID("mock") +func must[T any](t *testing.T, builder func() (T, error)) T { + t.Helper() + thing, err := builder() + require.NoError(t, err) + return thing +} + func TestGetServer(t *testing.T) { testCases := []struct { name string @@ -24,14 +31,18 @@ func TestGetServer(t *testing.T) { expected error }{ { - name: "obtain server authenticator", - authenticator: auth.NewServer(), - expected: nil, + name: "obtain server authenticator", + authenticator: must(t, func() (extension.Extension, error) { + return extensionauth.NewServer() + }), + expected: nil, }, { - name: "not a server authenticator", - authenticator: auth.NewClient(), - expected: errNotServer, + name: "not a server authenticator", + authenticator: must(t, func() (extension.Extension, error) { + return extensionauth.NewClient() + }), + expected: errNotServer, }, } for _, tt := range testCases { @@ -75,14 +86,18 @@ func TestGetClient(t *testing.T) { expected error }{ { - name: "obtain client authenticator", - authenticator: auth.NewClient(), - expected: nil, + name: "obtain client authenticator", + authenticator: must(t, func() (extension.Extension, error) { + return extensionauth.NewClient() + }), + expected: nil, }, { - name: "not a client authenticator", - authenticator: auth.NewServer(), - expected: errNotClient, + name: "not a client authenticator", + authenticator: must(t, func() (extension.Extension, error) { + return extensionauth.NewServer() + }), + expected: errNotClient, }, } for _, tt := range testCases { diff --git a/config/configauth/go.mod b/config/configauth/go.mod index eb90840a069..6e3b20e3a75 100644 --- a/config/configauth/go.mod +++ b/config/configauth/go.mod @@ -6,7 +6,7 @@ require ( github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/component v0.120.0 go.opentelemetry.io/collector/extension v0.120.0 - go.opentelemetry.io/collector/extension/auth v0.120.0 + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 go.uber.org/goleak v1.3.0 ) @@ -37,4 +37,4 @@ replace go.opentelemetry.io/collector/component/componenttest => ../../component replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth diff --git a/config/configgrpc/configgrpc.go b/config/configgrpc/configgrpc.go index 0576098a0f3..2947f2f3820 100644 --- a/config/configgrpc/configgrpc.go +++ b/config/configgrpc/configgrpc.go @@ -34,7 +34,7 @@ import ( "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/extension/auth" + "go.opentelemetry.io/collector/extension/extensionauth" ) var errMetadataNotFound = errors.New("no request metadata found") @@ -561,7 +561,7 @@ func contextWithClient(ctx context.Context, includeMetadata bool) context.Contex return client.NewContext(ctx, cl) } -func authUnaryServerInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler, server auth.Server) (any, error) { +func authUnaryServerInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler, server extensionauth.Server) (any, error) { headers, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, errMetadataNotFound @@ -575,7 +575,7 @@ func authUnaryServerInterceptor(ctx context.Context, req any, _ *grpc.UnaryServe return handler(ctx, req) } -func authStreamServerInterceptor(srv any, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler, server auth.Server) error { +func authStreamServerInterceptor(srv any, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler, server extensionauth.Server) error { ctx := stream.Context() headers, ok := metadata.FromIncomingContext(ctx) if !ok { diff --git a/config/configgrpc/configgrpc_test.go b/config/configgrpc/configgrpc_test.go index 2fdecb8ad77..ac598618e14 100644 --- a/config/configgrpc/configgrpc_test.go +++ b/config/configgrpc/configgrpc_test.go @@ -29,11 +29,18 @@ import ( "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/extension/auth/authtest" + "go.opentelemetry.io/collector/extension/extensionauth" + "go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest" "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" ) +func mustNewServerAuth(t *testing.T, opts ...extensionauth.ServerOption) extensionauth.Server { + t.Helper() + srv, err := extensionauth.NewServer(opts...) + require.NoError(t, err) + return srv +} + func TestNewDefaultKeepaliveClientConfig(t *testing.T) { expectedKeepaliveClientConfig := &KeepaliveClientConfig{ Time: time.Second * 10, @@ -164,7 +171,7 @@ func TestAllGrpcClientSettings(t *testing.T) { }, host: &mockHost{ ext: map[component.ID]component.Component{ - testAuthID: &authtest.MockClient{}, + testAuthID: &extensionauthtest.MockClient{}, }, }, }, @@ -193,7 +200,7 @@ func TestAllGrpcClientSettings(t *testing.T) { }, host: &mockHost{ ext: map[component.ID]component.Component{ - testAuthID: &authtest.MockClient{}, + testAuthID: &extensionauthtest.MockClient{}, }, }, }, @@ -222,7 +229,7 @@ func TestAllGrpcClientSettings(t *testing.T) { }, host: &mockHost{ ext: map[component.ID]component.Component{ - testAuthID: &authtest.MockClient{}, + testAuthID: &extensionauthtest.MockClient{}, }, }, }, @@ -399,9 +406,10 @@ func TestGrpcServerAuthSettings(t *testing.T) { gss.Auth = &configauth.Authentication{ AuthenticatorID: mockID, } + host := &mockHost{ ext: map[component.ID]component.Component{ - mockID: auth.NewServer(), + mockID: mustNewServerAuth(t), }, } srv, err := gss.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings()) @@ -976,7 +984,7 @@ func TestDefaultUnaryInterceptorAuthSucceeded(t *testing.T) { ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) // test - res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc))) + res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, mustNewServerAuth(t, extensionauth.WithServerAuthenticate(authFunc))) // verify assert.Nil(t, res) @@ -1000,7 +1008,7 @@ func TestDefaultUnaryInterceptorAuthFailure(t *testing.T) { ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) // test - res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc))) + res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, mustNewServerAuth(t, extensionauth.WithServerAuthenticate(authFunc))) // verify assert.Nil(t, res) @@ -1021,7 +1029,7 @@ func TestDefaultUnaryInterceptorMissingMetadata(t *testing.T) { } // test - res, err := authUnaryServerInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc))) + res, err := authUnaryServerInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, handler, mustNewServerAuth(t, extensionauth.WithServerAuthenticate(authFunc))) // verify assert.Nil(t, res) @@ -1052,7 +1060,7 @@ func TestDefaultStreamInterceptorAuthSucceeded(t *testing.T) { } // test - err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc))) + err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, mustNewServerAuth(t, extensionauth.WithServerAuthenticate(authFunc))) // verify require.NoError(t, err) @@ -1078,7 +1086,7 @@ func TestDefaultStreamInterceptorAuthFailure(t *testing.T) { } // test - err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc))) + err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, mustNewServerAuth(t, extensionauth.WithServerAuthenticate(authFunc))) // verify require.ErrorContains(t, err, expectedErr.Error()) // unfortunately, grpc errors don't wrap the original ones @@ -1101,7 +1109,7 @@ func TestDefaultStreamInterceptorMissingMetadata(t *testing.T) { } // test - err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc))) + err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, mustNewServerAuth(t, extensionauth.WithServerAuthenticate(authFunc))) // verify assert.Equal(t, errMetadataNotFound, err) diff --git a/config/configgrpc/go.mod b/config/configgrpc/go.mod index 7cab2486d7e..a70198d5789 100644 --- a/config/configgrpc/go.mod +++ b/config/configgrpc/go.mod @@ -13,8 +13,8 @@ require ( go.opentelemetry.io/collector/config/confignet v1.26.0 go.opentelemetry.io/collector/config/configopaque v1.26.0 go.opentelemetry.io/collector/config/configtls v1.26.0 - go.opentelemetry.io/collector/extension/auth v0.120.0 - go.opentelemetry.io/collector/extension/auth/authtest v0.120.0 + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 + go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest v0.0.0-20250227134758-6a98ee8b14a2 go.opentelemetry.io/collector/pdata v1.26.0 go.opentelemetry.io/collector/pdata/testdata v0.120.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 @@ -67,7 +67,7 @@ replace go.opentelemetry.io/collector/config/configtls => ../configtls replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/pdata => ../../pdata @@ -81,4 +81,4 @@ replace go.opentelemetry.io/collector/component/componenttest => ../../component replace go.opentelemetry.io/collector/consumer => ../../consumer -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index e6ca2ff0dd1..3fdc49534c8 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -29,7 +29,7 @@ import ( "go.opentelemetry.io/collector/config/confighttp/internal" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/extension/auth" + "go.opentelemetry.io/collector/extension/extensionauth" ) const ( @@ -537,7 +537,7 @@ func NewDefaultCORSConfig() *CORSConfig { return &CORSConfig{} } -func authInterceptor(next http.Handler, server auth.Server, requestParams []string) http.Handler { +func authInterceptor(next http.Handler, server extensionauth.Server, requestParams []string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sources := r.Header query := r.URL.Query() diff --git a/config/confighttp/confighttp_test.go b/config/confighttp/confighttp_test.go index da87d9daf09..3b2b47cc42e 100644 --- a/config/confighttp/confighttp_test.go +++ b/config/confighttp/confighttp_test.go @@ -30,8 +30,8 @@ import ( "go.opentelemetry.io/collector/config/configcompression" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/extension/auth/authtest" + "go.opentelemetry.io/collector/extension/extensionauth" + "go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest" ) type customRoundTripper struct{} @@ -42,6 +42,13 @@ func (c *customRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, nil } +func mustNewServerAuth(t *testing.T, opts ...extensionauth.ServerOption) extensionauth.Server { + t.Helper() + srv, err := extensionauth.NewServer(opts...) + require.NoError(t, err) + return srv +} + var ( testAuthID = component.MustNewID("testauth") mockID = component.MustNewID("mock") @@ -54,7 +61,7 @@ var ( func TestAllHTTPClientSettings(t *testing.T) { host := &mockHost{ ext: map[component.ID]component.Component{ - testAuthID: &authtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, + testAuthID: &extensionauthtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, }, } @@ -180,7 +187,7 @@ func TestAllHTTPClientSettings(t *testing.T) { func TestPartialHTTPClientSettings(t *testing.T) { host := &mockHost{ ext: map[component.ID]component.Component{ - testAuthID: &authtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, + testAuthID: &extensionauthtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, }, } @@ -348,7 +355,7 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { shouldErr: false, host: &mockHost{ ext: map[component.ID]component.Component{ - mockID: &authtest.MockClient{ + mockID: &extensionauthtest.MockClient{ ResultRoundTripper: &customRoundTripper{}, }, }, @@ -363,7 +370,7 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { shouldErr: true, host: &mockHost{ ext: map[component.ID]component.Component{ - mockID: &authtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, + mockID: &extensionauthtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, }, }, }, @@ -385,7 +392,7 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { shouldErr: false, host: &mockHost{ ext: map[component.ID]component.Component{ - mockID: &authtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, + mockID: &extensionauthtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, }, }, }, @@ -399,7 +406,7 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { shouldErr: false, host: &mockHost{ ext: map[component.ID]component.Component{ - mockID: &authtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, + mockID: &extensionauthtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, }, }, }, @@ -413,7 +420,7 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { shouldErr: false, host: &mockHost{ ext: map[component.ID]component.Component{ - mockID: &authtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, + mockID: &extensionauthtest.MockClient{ResultRoundTripper: &customRoundTripper{}}, }, }, }, @@ -426,7 +433,7 @@ func TestHTTPClientSettingWithAuthConfig(t *testing.T) { shouldErr: true, host: &mockHost{ ext: map[component.ID]component.Component{ - mockID: &authtest.MockClient{ + mockID: &extensionauthtest.MockClient{ ResultRoundTripper: &customRoundTripper{}, MustError: true, }, }, @@ -825,8 +832,8 @@ func TestHttpCorsWithSettings(t *testing.T) { host := &mockHost{ ext: map[component.ID]component.Component{ - mockID: auth.NewServer( - auth.WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + mockID: mustNewServerAuth(t, + extensionauth.WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { return ctx, errors.New("Settings failed") }), ), @@ -1137,8 +1144,8 @@ func TestServerAuth(t *testing.T) { host := &mockHost{ ext: map[component.ID]component.Component{ - mockID: auth.NewServer( - auth.WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + mockID: mustNewServerAuth(t, + extensionauth.WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { authCalled = true return ctx, nil }), @@ -1188,8 +1195,8 @@ func TestFailedServerAuth(t *testing.T) { } host := &mockHost{ ext: map[component.ID]component.Component{ - mockID: auth.NewServer( - auth.WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + mockID: mustNewServerAuth(t, + extensionauth.WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { return ctx, errors.New("Settings failed") }), ), @@ -1369,8 +1376,8 @@ func TestAuthWithQueryParams(t *testing.T) { host := &mockHost{ ext: map[component.ID]component.Component{ - mockID: auth.NewServer( - auth.WithServerAuthenticate(func(ctx context.Context, sources map[string][]string) (context.Context, error) { + mockID: mustNewServerAuth(t, + extensionauth.WithServerAuthenticate(func(ctx context.Context, sources map[string][]string) (context.Context, error) { require.Len(t, sources, 1) assert.Equal(t, "1", sources["auth"][0]) authCalled = true diff --git a/config/confighttp/go.mod b/config/confighttp/go.mod index a124d6ac21e..9013c8210a7 100644 --- a/config/confighttp/go.mod +++ b/config/confighttp/go.mod @@ -15,8 +15,8 @@ require ( go.opentelemetry.io/collector/config/configcompression v1.26.0 go.opentelemetry.io/collector/config/configopaque v1.26.0 go.opentelemetry.io/collector/config/configtls v1.26.0 - go.opentelemetry.io/collector/extension/auth v0.120.0 - go.opentelemetry.io/collector/extension/auth/authtest v0.120.0 + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 + go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest v0.0.0-20250227134758-6a98ee8b14a2 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 go.opentelemetry.io/otel v1.34.0 go.uber.org/goleak v1.3.0 @@ -59,7 +59,7 @@ replace go.opentelemetry.io/collector/config/configtls => ../configtls replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/pdata => ../../pdata @@ -71,4 +71,4 @@ replace go.opentelemetry.io/collector/consumer => ../../consumer replace go.opentelemetry.io/collector/client => ../../client -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest diff --git a/config/confighttp/xconfighttp/go.mod b/config/confighttp/xconfighttp/go.mod index fb1ee96a3a5..9b740936671 100644 --- a/config/confighttp/xconfighttp/go.mod +++ b/config/confighttp/xconfighttp/go.mod @@ -32,7 +32,7 @@ require ( go.opentelemetry.io/collector/config/configopaque v1.26.0 // indirect go.opentelemetry.io/collector/config/configtls v1.26.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/pdata v1.26.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect @@ -54,7 +54,7 @@ replace go.opentelemetry.io/collector/client => ../../../client replace go.opentelemetry.io/collector/consumer => ../../../consumer -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/component/componenttest => ../../../component/componenttest @@ -62,7 +62,7 @@ replace go.opentelemetry.io/collector/config/configauth => ../../configauth replace go.opentelemetry.io/collector/pdata => ../../../pdata -replace go.opentelemetry.io/collector/extension/auth => ../../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../../extension/extensionauth replace go.opentelemetry.io/collector/config/configopaque => ../../configopaque diff --git a/confmap/provider.go b/confmap/provider.go index 55d0d69341d..9718b692013 100644 --- a/confmap/provider.go +++ b/confmap/provider.go @@ -100,6 +100,7 @@ type ChangeEvent struct { // Retrieved holds the result of a call to the Retrieve method of a Provider object. type Retrieved struct { rawConf any + errorHint error closeFunc CloseFunc stringRepresentation string @@ -107,6 +108,7 @@ type Retrieved struct { } type retrievedSettings struct { + errorHint error stringRepresentation string isSetString bool closeFunc CloseFunc @@ -138,6 +140,12 @@ func withStringRepresentation(stringRepresentation string) RetrievedOption { }) } +func withErrorHint(errorHint error) RetrievedOption { + return retrievedOptionFunc(func(settings *retrievedSettings) { + settings.errorHint = errorHint + }) +} + // NewRetrievedFromYAML returns a new Retrieved instance that contains the deserialized data from the yaml bytes. // * yamlBytes the yaml bytes that will be deserialized. // * opts specifies options associated with this Retrieved value, such as CloseFunc. @@ -146,7 +154,10 @@ func NewRetrievedFromYAML(yamlBytes []byte, opts ...RetrievedOption) (*Retrieved if err := yaml.Unmarshal(yamlBytes, &rawConf); err != nil { // If the string is not valid YAML, we try to use it verbatim as a string. strRep := string(yamlBytes) - return NewRetrieved(strRep, append(opts, withStringRepresentation(strRep))...) + return NewRetrieved(strRep, append(opts, + withStringRepresentation(strRep), + withErrorHint(fmt.Errorf("assuming string type since contents are not valid YAML: %w", err)), + )...) } switch rawConf.(type) { @@ -175,6 +186,7 @@ func NewRetrieved(rawConf any, opts ...RetrievedOption) (*Retrieved, error) { } return &Retrieved{ rawConf: rawConf, + errorHint: set.errorHint, closeFunc: set.closeFunc, stringRepresentation: set.stringRepresentation, isSetString: set.isSetString, @@ -188,6 +200,9 @@ func (r *Retrieved) AsConf() (*Conf, error) { } val, ok := r.rawConf.(map[string]any) if !ok { + if r.errorHint != nil { + return nil, fmt.Errorf("retrieved value (type=%T) cannot be used as a Conf: %w", r.rawConf, r.errorHint) + } return nil, fmt.Errorf("retrieved value (type=%T) cannot be used as a Conf", r.rawConf) } return NewFromStringMap(val), nil diff --git a/confmap/provider/envprovider/README.md b/confmap/provider/envprovider/README.md new file mode 100644 index 00000000000..7e754448bc0 --- /dev/null +++ b/confmap/provider/envprovider/README.md @@ -0,0 +1,38 @@ +# Environment Variable Provider + + +| Status | | +| ------------- |-----------| +| Stability | [stable] | +| Distributions | [core], [contrib], [k8s], [otlp] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprovider%2Fenvprovider%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprovider%2Fenvprovider) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprovider%2Fenvprovider%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprovider%2Fenvprovider) | + +[stable]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stable +[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib +[k8s]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-k8s +[otlp]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-otlp + + +## Usage + +The scheme for this provider is `env`. Usage looks like the following: + +```text +env:NAME_OF_ENVIRONMENT_VARIABLE +``` + +To use default values when the environment variable has not been set, you can +include a suffix to specify it: + +```text +env:NAME_OF_ENVIRONMENT_VARIABLE:-default_value +``` + +Environment variables must match the following regular expression. That is, they +must be at least one character, start with a letter or underscore, and can only +include letters, numbers, and underscores. + +```text +^[a-zA-Z_][a-zA-Z0-9_]*$ +``` diff --git a/extension/auth/package_test.go b/confmap/provider/envprovider/generated_package_test.go similarity index 53% rename from extension/auth/package_test.go rename to confmap/provider/envprovider/generated_package_test.go index 205e87026a7..7d24d070940 100644 --- a/extension/auth/package_test.go +++ b/confmap/provider/envprovider/generated_package_test.go @@ -1,7 +1,6 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 +// Code generated by mdatagen. DO NOT EDIT. -package auth +package envprovider import ( "testing" diff --git a/confmap/provider/envprovider/metadata.yaml b/confmap/provider/envprovider/metadata.yaml new file mode 100644 index 00000000000..228ca45963d --- /dev/null +++ b/confmap/provider/envprovider/metadata.yaml @@ -0,0 +1,8 @@ +type: env +github_project: open-telemetry/opentelemetry-collector + +status: + class: provider + stability: + stable: [provider] + distributions: [core, contrib, k8s, otlp] diff --git a/confmap/provider/envprovider/provider.go b/confmap/provider/envprovider/provider.go index eab5e63f14b..cd21d427427 100644 --- a/confmap/provider/envprovider/provider.go +++ b/confmap/provider/envprovider/provider.go @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +//go:generate mdatagen metadata.yaml + package envprovider // import "go.opentelemetry.io/collector/confmap/provider/envprovider" import ( @@ -31,7 +33,7 @@ type provider struct { // A default value for unset variable can be provided after :- suffix, for example: // `env:NAME_OF_ENVIRONMENT_VARIABLE:-default_value` // -// See also: https://opentelemetry.io/docs/specs/otel/configuration/file-configuration/#environment-variable-substitution +// See also: https://opentelemetry.io/docs/specs/otel/configuration/data-model/#environment-variable-substitution func NewFactory() confmap.ProviderFactory { return confmap.NewProviderFactory(newProvider) } diff --git a/confmap/provider/fileprovider/README.md b/confmap/provider/fileprovider/README.md new file mode 100644 index 00000000000..d2e87c520f8 --- /dev/null +++ b/confmap/provider/fileprovider/README.md @@ -0,0 +1,27 @@ +# File Provider + + +| Status | | +| ------------- |-----------| +| Stability | [stable] | +| Distributions | [core], [contrib], [k8s], [otlp] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprovider%2Ffileprovider%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprovider%2Ffileprovider) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprovider%2Ffileprovider%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprovider%2Ffileprovider) | + +[stable]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stable +[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib +[k8s]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-k8s +[otlp]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-otlp + + +## Overview + +The File Provider takes paths to files and reads their contents as YAML to provide configuration to the Collector. + +## Usage + +The scheme for this provider is `file`. Usage looks like the following: + +```text +file:/path/to/file.yaml +``` diff --git a/extension/auth/authtest/package_test.go b/confmap/provider/fileprovider/generated_package_test.go similarity index 52% rename from extension/auth/authtest/package_test.go rename to confmap/provider/fileprovider/generated_package_test.go index b503d1f7ce2..a6cc23157ca 100644 --- a/extension/auth/authtest/package_test.go +++ b/confmap/provider/fileprovider/generated_package_test.go @@ -1,7 +1,6 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 +// Code generated by mdatagen. DO NOT EDIT. -package authtest +package fileprovider import ( "testing" diff --git a/confmap/provider/fileprovider/metadata.yaml b/confmap/provider/fileprovider/metadata.yaml new file mode 100644 index 00000000000..88737b2c84c --- /dev/null +++ b/confmap/provider/fileprovider/metadata.yaml @@ -0,0 +1,8 @@ +type: file +github_project: open-telemetry/opentelemetry-collector + +status: + class: provider + stability: + stable: [provider] + distributions: [core, contrib, k8s, otlp] diff --git a/confmap/provider/fileprovider/provider.go b/confmap/provider/fileprovider/provider.go index fdd1a8bb380..fe7dd12535d 100644 --- a/confmap/provider/fileprovider/provider.go +++ b/confmap/provider/fileprovider/provider.go @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +//go:generate mdatagen metadata.yaml + package fileprovider // import "go.opentelemetry.io/collector/confmap/provider/fileprovider" import ( diff --git a/confmap/provider/httpprovider/README.md b/confmap/provider/httpprovider/README.md index d3af07a4a0c..a25e36ae32b 100644 --- a/confmap/provider/httpprovider/README.md +++ b/confmap/provider/httpprovider/README.md @@ -1,13 +1,28 @@ -What is this new component httpprovider? -- An implementation of `confmap.Provider` for HTTP (httpprovider) allows OTEL Collector the ability to load configuration for itself by fetching and reading config files stored in HTTP servers. +# HTTP Provider -How this new component httpprovider works? -- It will be called by `confmap.Resolver` to load configurations for OTEL Collector. -- By giving a config URI starting with prefix 'http://', this httpprovider will be used to download config files from given HTTP URIs, and then used the downloaded config files to deploy the OTEL Collector. -- In our code, we check the validity scheme and string pattern of HTTP URIs. And also check if there are any problems on config downloading and config deserialization. + +| Status | | +| ------------- |-----------| +| Stability | [stable] | +| Distributions | [core], [contrib], [k8s] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprovider%2Fhttpprovider%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprovider%2Fhttpprovider) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprovider%2Fhttpprovider%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprovider%2Fhttpprovider) | -Expected URI format: -- http://... +[stable]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stable +[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib +[k8s]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-k8s + -Prerequisites: -- Need to setup a HTTP server ahead, which returns with a config files according to the given URI \ No newline at end of file +## Overview + +The HTTP Provider takes an HTTP URI to a file and reads its contents as YAML to provide configuration to the Collector. + +For HTTPS endpoints, please see the [HTTPS provider](../httpsprovider/README.md). + +## Usage + +The scheme for this provider is `http`. Usage looks like the following passed to the Collector's command line invocation: + +```text +--config=http://example.com/config.yaml +``` diff --git a/confmap/provider/httpprovider/package_test.go b/confmap/provider/httpprovider/generated_package_test.go similarity index 61% rename from confmap/provider/httpprovider/package_test.go rename to confmap/provider/httpprovider/generated_package_test.go index 0faa72a1207..7a6e2773fde 100644 --- a/confmap/provider/httpprovider/package_test.go +++ b/confmap/provider/httpprovider/generated_package_test.go @@ -1,5 +1,4 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 +// Code generated by mdatagen. DO NOT EDIT. package httpprovider diff --git a/confmap/provider/httpprovider/metadata.yaml b/confmap/provider/httpprovider/metadata.yaml new file mode 100644 index 00000000000..fd59d3db29e --- /dev/null +++ b/confmap/provider/httpprovider/metadata.yaml @@ -0,0 +1,8 @@ +type: http +github_project: open-telemetry/opentelemetry-collector + +status: + class: provider + stability: + stable: [provider] + distributions: [core, contrib, k8s] diff --git a/confmap/provider/httpprovider/provider.go b/confmap/provider/httpprovider/provider.go index c47c1df5d99..fab06efbeba 100644 --- a/confmap/provider/httpprovider/provider.go +++ b/confmap/provider/httpprovider/provider.go @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +//go:generate mdatagen metadata.yaml + package httpprovider // import "go.opentelemetry.io/collector/confmap/provider/httpprovider" import ( diff --git a/confmap/provider/httpsprovider/README.md b/confmap/provider/httpsprovider/README.md index 6e484ede527..1e94136c9b8 100644 --- a/confmap/provider/httpsprovider/README.md +++ b/confmap/provider/httpsprovider/README.md @@ -1,18 +1,37 @@ -### What is the httpsprovider? +# HTTPS Provider -An implementation of `confmap.Provider` for HTTPS (httpsprovider) allows OTEL Collector to use the HTTPS protocol to -load configuration files stored in web servers. + +| Status | | +| ------------- |-----------| +| Stability | [stable] | +| Distributions | [core], [contrib], [k8s] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprovider%2Fhttpsprovider%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprovider%2Fhttpsprovider) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprovider%2Fhttpsprovider%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprovider%2Fhttpsprovider) | -Expected URI format: -- https://... +[stable]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stable +[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib +[k8s]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-k8s + -### Prerequisites +## Overview -You need to setup a HTTP server with support to HTTPS. The server must have a certificate that can be validated in the -host running the collector using system root certificates. +The HTTPS Provider takes an HTTPS URI to a file and reads its contents as YAML +to provide configuration to the Collector. The validity of the certificate of +the HTTPS endpoint is verified when making the connection. -### Configuration +## Usage -At this moment, this component only support communicating with servers whose certificate can be verified using the root -CA certificates installed in the system. The process of adding more root CA certificates to the system is operating -system dependent. For Linux, please refer to the `update-ca-trust` command. +The scheme for this provider is `https`. Usage looks like the following passed +to the Collector's command line invocation: + +```text +--config=https://example.com/config.yaml +``` + +### Notes + +The provider currently only supports communicating with servers whose +certificate can be verified using the root CA certificates installed in the +system. The process of adding more root CA certificates to the system is +Operating System-dependent. For Linux, please refer to the `update-ca-trust` +command. diff --git a/confmap/provider/httpsprovider/package_test.go b/confmap/provider/httpsprovider/generated_package_test.go similarity index 62% rename from confmap/provider/httpsprovider/package_test.go rename to confmap/provider/httpsprovider/generated_package_test.go index 7ed3dc83e10..08eb81fe050 100644 --- a/confmap/provider/httpsprovider/package_test.go +++ b/confmap/provider/httpsprovider/generated_package_test.go @@ -1,5 +1,4 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 +// Code generated by mdatagen. DO NOT EDIT. package httpsprovider diff --git a/confmap/provider/httpsprovider/metadata.yaml b/confmap/provider/httpsprovider/metadata.yaml new file mode 100644 index 00000000000..5ae39478456 --- /dev/null +++ b/confmap/provider/httpsprovider/metadata.yaml @@ -0,0 +1,8 @@ +type: https +github_project: open-telemetry/opentelemetry-collector + +status: + class: provider + stability: + stable: [provider] + distributions: [core, contrib, k8s] diff --git a/confmap/provider/httpsprovider/provider.go b/confmap/provider/httpsprovider/provider.go index 579c15babf2..99c7b44bade 100644 --- a/confmap/provider/httpsprovider/provider.go +++ b/confmap/provider/httpsprovider/provider.go @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +//go:generate mdatagen metadata.yaml + package httpsprovider // import "go.opentelemetry.io/collector/confmap/provider/httpsprovider" import ( diff --git a/confmap/provider/yamlprovider/README.md b/confmap/provider/yamlprovider/README.md new file mode 100644 index 00000000000..7bce403f861 --- /dev/null +++ b/confmap/provider/yamlprovider/README.md @@ -0,0 +1,26 @@ +# YAML Provider + + +| Status | | +| ------------- |-----------| +| Stability | [stable] | +| Distributions | [core], [contrib], [k8s] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprovider%2Fyamlprovider%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprovider%2Fyamlprovider) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprovider%2Fyamlprovider%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprovider%2Fyamlprovider) | + +[stable]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stable +[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib +[k8s]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-k8s + + +## Overview + +The YAML Provider takes a literal YAML string as Collector configuration. + +## Usage + +The scheme for this provider is `yaml`. Usage looks like the following passed to the Collector's command line invocation: + +```text +--config=yaml:processors::batch::timeout: 2s +``` diff --git a/confmap/provider/yamlprovider/package_test.go b/confmap/provider/yamlprovider/generated_package_test.go similarity index 61% rename from confmap/provider/yamlprovider/package_test.go rename to confmap/provider/yamlprovider/generated_package_test.go index 50b62480e04..52cc6e002f8 100644 --- a/confmap/provider/yamlprovider/package_test.go +++ b/confmap/provider/yamlprovider/generated_package_test.go @@ -1,5 +1,4 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 +// Code generated by mdatagen. DO NOT EDIT. package yamlprovider diff --git a/confmap/provider/yamlprovider/metadata.yaml b/confmap/provider/yamlprovider/metadata.yaml new file mode 100644 index 00000000000..e3ffcf28a0e --- /dev/null +++ b/confmap/provider/yamlprovider/metadata.yaml @@ -0,0 +1,8 @@ +type: yaml +github_project: open-telemetry/opentelemetry-collector + +status: + class: provider + stability: + stable: [provider] + distributions: [core, contrib, k8s] diff --git a/confmap/provider/yamlprovider/provider.go b/confmap/provider/yamlprovider/provider.go index 723643d3056..5f4d1a55abc 100644 --- a/confmap/provider/yamlprovider/provider.go +++ b/confmap/provider/yamlprovider/provider.go @@ -3,6 +3,8 @@ package yamlprovider // import "go.opentelemetry.io/collector/confmap/provider/yamlprovider" +//go:generate mdatagen metadata.yaml + import ( "context" "fmt" diff --git a/confmap/provider_test.go b/confmap/provider_test.go index 07635ee7fc2..70a47f3f522 100644 --- a/confmap/provider_test.go +++ b/confmap/provider_test.go @@ -134,7 +134,9 @@ func TestNewRetrievedFromYAMLInvalidYAMLBytes(t *testing.T) { require.NoError(t, err) _, err = ret.AsConf() - require.Error(t, err) + require.EqualError(t, err, + "retrieved value (type=string) cannot be used as a Conf: assuming string type since contents are not valid YAML: yaml: line 1: did not find expected node content", + ) str, err := ret.AsString() require.NoError(t, err) @@ -150,7 +152,7 @@ func TestNewRetrievedFromYAMLInvalidAsMap(t *testing.T) { require.NoError(t, err) _, err = ret.AsConf() - require.Error(t, err) + require.EqualError(t, err, "retrieved value (type=string) cannot be used as a Conf") str, err := ret.AsString() require.NoError(t, err) diff --git a/connector/connector.go b/connector/connector.go index ae930f30626..9c9f113b64a 100644 --- a/connector/connector.go +++ b/connector/connector.go @@ -305,6 +305,11 @@ func (f *factory) CreateTracesToTraces(ctx context.Context, set Settings, cfg co if f.createTracesToTracesFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalTraces, pipeline.SignalTraces) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createTracesToTracesFunc(ctx, set, cfg, next) } @@ -312,6 +317,11 @@ func (f *factory) CreateTracesToMetrics(ctx context.Context, set Settings, cfg c if f.createTracesToMetricsFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalTraces, pipeline.SignalMetrics) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createTracesToMetricsFunc(ctx, set, cfg, next) } @@ -319,6 +329,11 @@ func (f *factory) CreateTracesToLogs(ctx context.Context, set Settings, cfg comp if f.createTracesToLogsFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalTraces, pipeline.SignalLogs) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createTracesToLogsFunc(ctx, set, cfg, next) } @@ -326,6 +341,11 @@ func (f *factory) CreateMetricsToTraces(ctx context.Context, set Settings, cfg c if f.createMetricsToTracesFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalMetrics, pipeline.SignalTraces) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createMetricsToTracesFunc(ctx, set, cfg, next) } @@ -333,6 +353,11 @@ func (f *factory) CreateMetricsToMetrics(ctx context.Context, set Settings, cfg if f.createMetricsToMetricsFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalMetrics, pipeline.SignalMetrics) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createMetricsToMetricsFunc(ctx, set, cfg, next) } @@ -340,6 +365,11 @@ func (f *factory) CreateMetricsToLogs(ctx context.Context, set Settings, cfg com if f.createMetricsToLogsFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalMetrics, pipeline.SignalLogs) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createMetricsToLogsFunc(ctx, set, cfg, next) } @@ -347,6 +377,11 @@ func (f *factory) CreateLogsToTraces(ctx context.Context, set Settings, cfg comp if f.createLogsToTracesFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalLogs, pipeline.SignalTraces) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createLogsToTracesFunc(ctx, set, cfg, next) } @@ -354,6 +389,11 @@ func (f *factory) CreateLogsToMetrics(ctx context.Context, set Settings, cfg com if f.createLogsToMetricsFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalLogs, pipeline.SignalMetrics) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createLogsToMetricsFunc(ctx, set, cfg, next) } @@ -361,6 +401,11 @@ func (f *factory) CreateLogsToLogs(ctx context.Context, set Settings, cfg compon if f.createLogsToLogsFunc == nil { return nil, internal.ErrDataTypes(set.ID, pipeline.SignalLogs, pipeline.SignalLogs) } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createLogsToLogsFunc(ctx, set, cfg, next) } diff --git a/connector/connector_test.go b/connector/connector_test.go index 28b59dc78bd..1853c9295f8 100644 --- a/connector/connector_test.go +++ b/connector/connector_test.go @@ -19,7 +19,7 @@ import ( var ( testType = component.MustNewType("test") - testID = component.MustNewIDWithName("type", "name") + testID = component.MustNewIDWithName("test", "name") ) func TestNewFactoryNoOptions(t *testing.T) { @@ -58,18 +58,26 @@ func TestNewFactoryWithSameTypes(t *testing.T) { WithLogsToLogs(createLogsToLogs, component.StabilityLevelUnmaintained)) assert.EqualValues(t, testType, factory.Type()) assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) + wrongID := component.MustNewID("wrong") + wrongIDErrStr := internal.ErrIDMismatch(wrongID, testType).Error() assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToTracesStability()) _, err := factory.CreateTracesToTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) + _, err = factory.CreateTracesToTraces(context.Background(), Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + require.ErrorContains(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToMetricsStability()) _, err = factory.CreateMetricsToMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) + _, err = factory.CreateMetricsToMetrics(context.Background(), Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + require.ErrorContains(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToLogsStability()) _, err = factory.CreateLogsToLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) + _, err = factory.CreateLogsToLogs(context.Background(), Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + require.ErrorContains(t, err, wrongIDErrStr) _, err = factory.CreateTracesToMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) assert.Equal(t, err, internal.ErrDataTypes(testID, pipeline.SignalTraces, pipeline.SignalMetrics)) @@ -147,33 +155,33 @@ func TestNewFactoryWithAllTypes(t *testing.T) { assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToTracesStability()) - _, err := factory.CreateTracesToTraces(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err := factory.CreateTracesToTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelDevelopment, factory.TracesToMetricsStability()) - _, err = factory.CreateTracesToMetrics(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateTracesToMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToLogsStability()) - _, err = factory.CreateTracesToLogs(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateTracesToLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToTracesStability()) - _, err = factory.CreateMetricsToTraces(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateMetricsToTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToMetricsStability()) - _, err = factory.CreateMetricsToMetrics(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateMetricsToMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelStable, factory.MetricsToLogsStability()) - _, err = factory.CreateMetricsToLogs(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateMetricsToLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelDeprecated, factory.LogsToTracesStability()) - _, err = factory.CreateLogsToTraces(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateLogsToTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToMetricsStability()) - _, err = factory.CreateLogsToMetrics(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateLogsToMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToLogsStability()) - _, err = factory.CreateLogsToLogs(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = factory.CreateLogsToLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) assert.NoError(t, err) } diff --git a/connector/internal/factory.go b/connector/internal/factory.go index b865f6311cb..764ba43fdee 100644 --- a/connector/internal/factory.go +++ b/connector/internal/factory.go @@ -13,3 +13,7 @@ import ( func ErrDataTypes(id component.ID, from, to pipeline.Signal) error { return fmt.Errorf("connector %q cannot connect from %s to %s: %w", id, from, to, pipeline.ErrSignalNotSupported) } + +func ErrIDMismatch(id component.ID, typ component.Type) error { + return fmt.Errorf("component type mismatch: component ID %q does not have type %q", id, typ) +} diff --git a/docs/coding-guidelines.md b/docs/coding-guidelines.md index 79e755dd0c4..6cd1ec29d22 100644 --- a/docs/coding-guidelines.md +++ b/docs/coding-guidelines.md @@ -61,6 +61,12 @@ We use the following rules for some common situations where we split into separa 1. Consider splitting into separate modules if the API may evolve independently in separate groups of packages. For example, the configuration related to HTTP and gRPC evolve independently, so `config/configgrpc` and `config/confighttp` are separate modules. +1. For component names, add the component kind as a suffix for the module name. For example, the + OTLP receiver is in the `receiver/otlpreceiver` module. +1. Modules that add specific functionality related to a parent folder should have a prefix in the + name that relates to the parent module. For example, `configauth` has the `config` prefix since + it is part of the `config` folder, and `extensionauth` has `extension` as a prefix since it is + part of the `extension` module. 1. Testing helpers should be in a separate submodule with the suffix `test`. For example, if you have a module `component`, the helpers should be in `component/componenttest`. 1. Experimental packages that will later be added to another module should be in their own module, diff --git a/exporter/exporter.go b/exporter/exporter.go index c66e27397c9..6542d9ef802 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -8,6 +8,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/exporter/internal/experr" "go.opentelemetry.io/collector/pipeline" ) @@ -132,6 +133,10 @@ func (f *factory) CreateTraces(ctx context.Context, set Settings, cfg component. return nil, pipeline.ErrSignalNotSupported } + if set.ID.Type() != f.Type() { + return nil, experr.ErrIDMismatch(set.ID, f.Type()) + } + return f.createTracesFunc(ctx, set, cfg) } @@ -140,6 +145,10 @@ func (f *factory) CreateMetrics(ctx context.Context, set Settings, cfg component return nil, pipeline.ErrSignalNotSupported } + if set.ID.Type() != f.Type() { + return nil, experr.ErrIDMismatch(set.ID, f.Type()) + } + return f.createMetricsFunc(ctx, set, cfg) } @@ -148,6 +157,10 @@ func (f *factory) CreateLogs(ctx context.Context, set Settings, cfg component.Co return nil, pipeline.ErrSignalNotSupported } + if set.ID.Type() != f.Type() { + return nil, experr.ErrIDMismatch(set.ID, f.Type()) + } + return f.createLogsFunc(ctx, set, cfg) } diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index 79a9db0ef67..4fbf0c9c990 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -12,27 +12,31 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/exporter/internal/experr" "go.opentelemetry.io/collector/pipeline" ) +var ( + testType = component.MustNewType("test") + testID = component.NewID(testType) +) + func TestNewFactory(t *testing.T) { - testType := component.MustNewType("test") defaultCfg := struct{}{} f := NewFactory( testType, func() component.Config { return &defaultCfg }) assert.EqualValues(t, testType, f.Type()) assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig()) - _, err := f.CreateTraces(context.Background(), Settings{}, &defaultCfg) + _, err := f.CreateTraces(context.Background(), Settings{ID: testID}, &defaultCfg) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) - _, err = f.CreateMetrics(context.Background(), Settings{}, &defaultCfg) + _, err = f.CreateMetrics(context.Background(), Settings{ID: testID}, &defaultCfg) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) - _, err = f.CreateLogs(context.Background(), Settings{}, &defaultCfg) + _, err = f.CreateLogs(context.Background(), Settings{ID: testID}, &defaultCfg) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) } func TestNewFactoryWithOptions(t *testing.T) { - testType := component.MustNewType("test") defaultCfg := struct{}{} f := NewFactory( testType, @@ -43,17 +47,26 @@ func TestNewFactoryWithOptions(t *testing.T) { assert.EqualValues(t, testType, f.Type()) assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig()) + wrongID := component.MustNewID("wrong") + wrongIDErrStr := experr.ErrIDMismatch(wrongID, testType).Error() + assert.Equal(t, component.StabilityLevelDevelopment, f.TracesStability()) - _, err := f.CreateTraces(context.Background(), Settings{}, &defaultCfg) + _, err := f.CreateTraces(context.Background(), Settings{ID: testID}, &defaultCfg) require.NoError(t, err) + _, err = f.CreateTraces(context.Background(), Settings{ID: wrongID}, &defaultCfg) + require.EqualError(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelAlpha, f.MetricsStability()) - _, err = f.CreateMetrics(context.Background(), Settings{}, &defaultCfg) + _, err = f.CreateMetrics(context.Background(), Settings{ID: testID}, &defaultCfg) require.NoError(t, err) + _, err = f.CreateMetrics(context.Background(), Settings{ID: wrongID}, &defaultCfg) + require.EqualError(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelDeprecated, f.LogsStability()) - _, err = f.CreateLogs(context.Background(), Settings{}, &defaultCfg) - assert.NoError(t, err) + _, err = f.CreateLogs(context.Background(), Settings{ID: testID}, &defaultCfg) + require.NoError(t, err) + _, err = f.CreateLogs(context.Background(), Settings{ID: wrongID}, &defaultCfg) + require.EqualError(t, err, wrongIDErrStr) } var nopInstance = &nop{ diff --git a/exporter/exporterbatcher/config.go b/exporter/exporterbatcher/config.go index 922a051c0fc..4a3ef0d3789 100644 --- a/exporter/exporterbatcher/config.go +++ b/exporter/exporterbatcher/config.go @@ -6,10 +6,11 @@ package exporterbatcher // import "go.opentelemetry.io/collector/exporter/export import ( "errors" "time" + + "go.opentelemetry.io/collector/confmap" ) // Config defines a configuration for batching requests based on a timeout and a minimum number of items. -// MaxSizeItems defines batch splitting functionality if it's more than zero. // Experimental: This API is at the early stage of development and may change without backward compatibility // until https://github.com/open-telemetry/opentelemetry-collector/issues/8122 is resolved. type Config struct { @@ -19,66 +20,70 @@ type Config struct { // FlushTimeout sets the time after which a batch will be sent regardless of its size. FlushTimeout time.Duration `mapstructure:"flush_timeout"` + // SizeConfig sets the size limits for a batch. SizeConfig `mapstructure:",squash"` - // Deprecated. Ignored if SizeConfig is set. + // Deprecated: [v0.121.0] Ignored if SizeConfig is set. MinSizeConfig `mapstructure:",squash"` - // Deprecated. Ignored if SizeConfig is set. + // Deprecated: [v0.121.0] Ignored if SizeConfig is set. MaxSizeConfig `mapstructure:",squash"` } +// SizeConfig sets the size limits for a batch. type SizeConfig struct { Sizer SizerType `mapstructure:"sizer"` - MinSize int `mapstructure:"mix_size"` + // MinSize defines the configuration for the minimum size of a batch. + MinSize int `mapstructure:"min_size"` + // MaxSize defines the configuration for the maximum size of a batch. MaxSize int `mapstructure:"max_size"` } -// MinSizeConfig defines the configuration for the minimum number of items in a batch. -// Experimental: This API is at the early stage of development and may change without backward compatibility -// until https://github.com/open-telemetry/opentelemetry-collector/issues/8122 is resolved. +// Deprecated: [v0.121.0] use SizeConfig. type MinSizeConfig struct { - // MinSizeItems is the number of items (spans, data points or log records for OTLP) at which the batch should be - // sent regardless of the timeout. There is no guarantee that the batch size always greater than this value. - // This option requires the Request to implement RequestItemsCounter interface. Otherwise, it will be ignored. - MinSizeItems int `mapstructure:"min_size_items"` + // Deprecated: [v0.121.0] use SizeConfig.MinSize. + MinSizeItems *int `mapstructure:"min_size_items"` } -// MaxSizeConfig defines the configuration for the maximum number of items in a batch. -// Experimental: This API is at the early stage of development and may change without backward compatibility -// until https://github.com/open-telemetry/opentelemetry-collector/issues/8122 is resolved. +// Deprecated: [v0.121.0] use SizeConfig. type MaxSizeConfig struct { - // MaxSizeItems is the maximum number of the batch items, i.e. spans, data points or log records for OTLP. - // If the batch size exceeds this value, it will be broken up into smaller batches if possible. - // Setting this value to zero disables the maximum size limit. - MaxSizeItems int `mapstructure:"max_size_items"` + // Deprecated: [v0.121.0] use SizeConfig.MaxSize. + MaxSizeItems *int `mapstructure:"max_size_items"` } -func (c Config) Validate() error { - if c.MinSizeItems < 0 { - return errors.New("min_size_items must be greater than or equal to zero") +func (c *Config) Unmarshal(conf *confmap.Conf) error { + if err := conf.Unmarshal(c); err != nil { + return err } - if c.MaxSizeItems < 0 { - return errors.New("max_size_items must be greater than or equal to zero") + + if c.MinSizeItems != nil && !conf.IsSet("min_size") { + c.SizeConfig.MinSize = *c.MinSizeItems } - if c.MaxSizeItems != 0 && c.MaxSizeItems < c.MinSizeItems { - return errors.New("max_size_items must be greater than or equal to min_size_items") + + if c.MaxSizeItems != nil && !conf.IsSet("max_size") { + c.SizeConfig.MaxSize = *c.MaxSizeItems } + + return nil +} + +func (c *Config) Validate() error { if c.FlushTimeout <= 0 { - return errors.New("timeout must be greater than zero") + return errors.New("`flush_timeout` must be greater than zero") } + return nil } func (c SizeConfig) Validate() error { if c.MinSize < 0 { - return errors.New("min_size must be greater than or equal to zero") + return errors.New("`min_size` must be greater than or equal to zero") } if c.MaxSize < 0 { - return errors.New("max_size must be greater than or equal to zero") + return errors.New("`max_size` must be greater than or equal to zero") } if c.MaxSize != 0 && c.MaxSize < c.MinSize { - return errors.New("max_size must be greater than or equal to mix_size") + return errors.New("`max_size` must be greater than or equal to mix_size") } return nil } @@ -87,8 +92,9 @@ func NewDefaultConfig() Config { return Config{ Enabled: true, FlushTimeout: 200 * time.Millisecond, - MinSizeConfig: MinSizeConfig{ - MinSizeItems: 8192, + SizeConfig: SizeConfig{ + Sizer: SizerTypeItems, + MinSize: 8192, }, } } diff --git a/exporter/exporterbatcher/config_test.go b/exporter/exporterbatcher/config_test.go index 742950c9aad..90b4c2fe4aa 100644 --- a/exporter/exporterbatcher/config_test.go +++ b/exporter/exporterbatcher/config_test.go @@ -6,9 +6,7 @@ package exporterbatcher import ( "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v2" "go.opentelemetry.io/collector/confmap" ) @@ -17,20 +15,9 @@ func TestValidateConfig(t *testing.T) { cfg := NewDefaultConfig() require.NoError(t, cfg.Validate()) - cfg.MinSizeItems = -1 - require.EqualError(t, cfg.Validate(), "min_size_items must be greater than or equal to zero") - cfg = NewDefaultConfig() cfg.FlushTimeout = 0 - require.EqualError(t, cfg.Validate(), "timeout must be greater than zero") - - cfg.MaxSizeItems = -1 - require.EqualError(t, cfg.Validate(), "max_size_items must be greater than or equal to zero") - - cfg = NewDefaultConfig() - cfg.MaxSizeItems = 20000 - cfg.MinSizeItems = 20001 - assert.EqualError(t, cfg.Validate(), "max_size_items must be greater than or equal to min_size_items") + require.EqualError(t, cfg.Validate(), "`flush_timeout` must be greater than zero") } func TestValidateSizeConfig(t *testing.T) { @@ -39,45 +26,88 @@ func TestValidateSizeConfig(t *testing.T) { MaxSize: -100, MinSize: 100, } - require.EqualError(t, cfg.Validate(), "max_size must be greater than or equal to zero") + require.EqualError(t, cfg.Validate(), "`max_size` must be greater than or equal to zero") cfg = SizeConfig{ - Sizer: SizerTypeBytes, + Sizer: SizerTypeItems, MaxSize: 100, MinSize: -100, } - require.EqualError(t, cfg.Validate(), "min_size must be greater than or equal to zero") + require.EqualError(t, cfg.Validate(), "`min_size` must be greater than or equal to zero") cfg = SizeConfig{ - Sizer: SizerTypeBytes, + Sizer: SizerTypeItems, MaxSize: 100, MinSize: 200, } - require.EqualError(t, cfg.Validate(), "max_size must be greater than or equal to mix_size") + require.EqualError(t, cfg.Validate(), "`max_size` must be greater than or equal to mix_size") } -func TestSizeUnmarshaler(t *testing.T) { - var rawConf map[string]any - cfg := NewDefaultConfig() - - require.NoError(t, yaml.Unmarshal([]byte(`sizer: bytes`), &rawConf)) - require.NoError(t, confmap.NewFromStringMap(rawConf).Unmarshal(&cfg)) - require.NoError(t, cfg.Validate()) - - require.NoError(t, yaml.Unmarshal([]byte(`sizer: "bytes"`), &rawConf)) - require.NoError(t, confmap.NewFromStringMap(rawConf).Unmarshal(&cfg)) - require.NoError(t, cfg.Validate()) - - require.NoError(t, yaml.Unmarshal([]byte(`sizer: items`), &rawConf)) - require.NoError(t, confmap.NewFromStringMap(rawConf).Unmarshal(&cfg)) - require.NoError(t, cfg.Validate()) - - require.NoError(t, yaml.Unmarshal([]byte(`sizer: 'items'`), &rawConf)) - require.NoError(t, confmap.NewFromStringMap(rawConf).Unmarshal(&cfg)) - require.NoError(t, cfg.Validate()) - - require.NoError(t, yaml.Unmarshal([]byte(`sizer: invalid`), &rawConf)) - require.EqualError(t, - confmap.NewFromStringMap(rawConf).Unmarshal(&cfg), - "decoding failed due to the following error(s):\n\nerror decoding 'sizer': invalid sizer: \"invalid\"") +func TestUnmarshalDeprecatedFields(t *testing.T) { + p111 := 111 + p222 := 222 + tests := []struct { + name string + input map[string]any + expected Config + }{ + { + name: "only_deprecated_fields_used", + input: map[string]any{ + "enabled": true, + "flush_timeout": 200, + "min_size_items": 111, + "max_size_items": 222, + }, + expected: Config{ + Enabled: true, + FlushTimeout: 200, + SizeConfig: SizeConfig{ + Sizer: SizerTypeItems, + MinSize: 111, + MaxSize: 222, + }, + MinSizeConfig: MinSizeConfig{ + MinSizeItems: &p111, + }, + MaxSizeConfig: MaxSizeConfig{ + MaxSizeItems: &p222, + }, + }, + }, + { + name: "both_new_and_deprecated_fields_used", + input: map[string]any{ + "enabled": true, + "flush_timeout": 200, + "min_size_items": 111, + "max_size_items": 222, + "min_size": 11, + "max_size": 22, + }, + expected: Config{ + Enabled: true, + FlushTimeout: 200, + SizeConfig: SizeConfig{ + Sizer: SizerTypeItems, + MinSize: 11, + MaxSize: 22, + }, + MinSizeConfig: MinSizeConfig{ + MinSizeItems: &p111, + }, + MaxSizeConfig: MaxSizeConfig{ + MaxSizeItems: &p222, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cfg := NewDefaultConfig() + require.NoError(t, cfg.Unmarshal(confmap.NewFromStringMap(test.input))) + require.Equal(t, test.expected, cfg) + require.NoError(t, cfg.Validate()) + }) + } } diff --git a/exporter/exporterbatcher/sizer_type.go b/exporter/exporterbatcher/sizer_type.go index 1b1b05a2769..fb93398eeba 100644 --- a/exporter/exporterbatcher/sizer_type.go +++ b/exporter/exporterbatcher/sizer_type.go @@ -7,20 +7,26 @@ import ( "fmt" ) -type SizerType string +type SizerType struct { + val string +} const ( - SizerTypeItems SizerType = "items" - SizerTypeBytes SizerType = "bytes" + sizerTypeItems = "items" + sizerTypeBytes = "bytes" +) + +var ( + SizerTypeItems = SizerType{val: sizerTypeItems} + SizerTypeBytes = SizerType{val: sizerTypeBytes} ) // UnmarshalText implements TextUnmarshaler interface. func (s *SizerType) UnmarshalText(text []byte) error { switch str := string(text); str { - case string(SizerTypeItems): + case sizerTypeItems: *s = SizerTypeItems - case string(SizerTypeBytes): - *s = SizerTypeBytes + // TODO support setting sizer to SizerTypeBytes when all logs, traces, and metrics batching support it default: return fmt.Errorf("invalid sizer: %q", str) } diff --git a/exporter/exporterbatcher/sizer_type_test.go b/exporter/exporterbatcher/sizer_type_test.go new file mode 100644 index 00000000000..f5dc14816c8 --- /dev/null +++ b/exporter/exporterbatcher/sizer_type_test.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package exporterbatcher + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" + + "go.opentelemetry.io/collector/confmap" +) + +func TestSizeTypeUnmarshaler(t *testing.T) { + var rawConf map[string]any + cfg := NewDefaultConfig() + + require.NoError(t, yaml.Unmarshal([]byte(`sizer: items`), &rawConf)) + require.NoError(t, confmap.NewFromStringMap(rawConf).Unmarshal(&cfg)) + require.NoError(t, cfg.Validate()) + + require.NoError(t, yaml.Unmarshal([]byte(`sizer: 'items'`), &rawConf)) + require.NoError(t, confmap.NewFromStringMap(rawConf).Unmarshal(&cfg)) + require.NoError(t, cfg.Validate()) + + require.NoError(t, yaml.Unmarshal([]byte(`sizer: invalid`), &rawConf)) + require.ErrorContains(t, + confmap.NewFromStringMap(rawConf).Unmarshal(&cfg), + "decoding failed due to the following error(s):\n\nerror decoding 'sizer': invalid sizer: \"invalid\"") +} diff --git a/exporter/exporterhelper/internal/base_exporter.go b/exporter/exporterhelper/internal/base_exporter.go index e8b8ffc4e43..579b0e310d6 100644 --- a/exporter/exporterhelper/internal/base_exporter.go +++ b/exporter/exporterhelper/internal/base_exporter.go @@ -6,11 +6,7 @@ package internal // import "go.opentelemetry.io/collector/exporter/exporterhelpe import ( "context" "errors" - "testing" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel/codes" - sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.uber.org/multierr" "go.uber.org/zap" @@ -42,9 +38,8 @@ type BaseExporter struct { // Chain of senders that the exporter helper applies before passing the data to the actual exporter. // The data is handled by each sender in the respective order starting from the queueSender. // Most of the senders are optional, and initialized with a no-op path-through sender. - QueueSender Sender[request.Request] - ObsrepSender Sender[request.Request] - RetrySender Sender[request.Request] + QueueSender Sender[request.Request] + RetrySender Sender[request.Request] firstSender Sender[request.Request] @@ -70,6 +65,11 @@ func NewBaseExporter(set exporter.Settings, signal pipeline.Signal, options ...O } } + //nolint: staticcheck + if be.batcherCfg.MinSizeItems != nil || be.batcherCfg.MaxSizeItems != nil { + set.Logger.Warn("Using of deprecated fields `min_size_items` and `max_size_items`") + } + // Consumer Sender is always initialized. be.firstSender = newSender(func(ctx context.Context, req request.Request) error { return req.Export(ctx) @@ -87,11 +87,10 @@ func NewBaseExporter(set exporter.Settings, signal pipeline.Signal, options ...O } var err error - be.ObsrepSender, err = newObsReportSender(set, signal, be.firstSender) + be.firstSender, err = newObsReportSender(set, signal, be.firstSender) if err != nil { return nil, err } - be.firstSender = be.ObsrepSender if be.batcherCfg.Enabled { // Batcher mutates the data. @@ -281,12 +280,3 @@ func WithUnmarshaler(unmarshaler exporterqueue.Unmarshaler[request.Request]) Opt return nil } } - -func CheckStatus(t *testing.T, sd sdktrace.ReadOnlySpan, err error) { - if err != nil { - require.Equal(t, codes.Error, sd.Status().Code, "SpanData %v", sd) - require.EqualError(t, err, sd.Status().Description, "SpanData %v", sd) - } else { - require.Equal(t, codes.Unset, sd.Status().Code, "SpanData %v", sd) - } -} diff --git a/exporter/exporterhelper/internal/batcher/default_batcher.go b/exporter/exporterhelper/internal/batcher/default_batcher.go index 2fd9497d323..f147b2c099b 100644 --- a/exporter/exporterhelper/internal/batcher/default_batcher.go +++ b/exporter/exporterhelper/internal/batcher/default_batcher.go @@ -67,7 +67,7 @@ func (qb *defaultBatcher) Consume(ctx context.Context, req request.Request, done var reqList []request.Request var mergeSplitErr error if qb.currentBatch == nil { - reqList, mergeSplitErr = req.MergeSplit(ctx, qb.batchCfg.MaxSizeConfig, nil) + reqList, mergeSplitErr = req.MergeSplit(ctx, qb.batchCfg.SizeConfig, nil) if mergeSplitErr != nil || len(reqList) == 0 { done.OnDone(mergeSplitErr) qb.currentBatchMu.Unlock() @@ -80,9 +80,9 @@ func (qb *defaultBatcher) Consume(ctx context.Context, req request.Request, done } // We have at least one result in the reqList. Last in the list may not have enough data to be flushed. - // Find if it has at least MinSizeItems, and if it does then move that as the current batch. + // Find if it has at least MinSize, and if it does then move that as the current batch. lastReq := reqList[len(reqList)-1] - if lastReq.ItemsCount() < qb.batchCfg.MinSizeItems { + if lastReq.ItemsCount() < qb.batchCfg.MinSize { // Do not flush the last item and add it to the current batch. reqList = reqList[:len(reqList)-1] qb.currentBatch = &batch{ @@ -101,7 +101,7 @@ func (qb *defaultBatcher) Consume(ctx context.Context, req request.Request, done return } - reqList, mergeSplitErr = qb.currentBatch.req.MergeSplit(ctx, qb.batchCfg.MaxSizeConfig, req) + reqList, mergeSplitErr = qb.currentBatch.req.MergeSplit(ctx, qb.batchCfg.SizeConfig, req) // If failed to merge signal all Done callbacks from current batch as well as the current request and reset the current batch. if mergeSplitErr != nil || len(reqList) == 0 { done.OnDone(mergeSplitErr) @@ -127,7 +127,7 @@ func (qb *defaultBatcher) Consume(ctx context.Context, req request.Request, done // cannot unlock and re-lock because we are not done processing all the responses. var firstBatch *batch // Need to check the currentBatch if more than 1 result returned or if 1 result return but larger than MinSize. - if len(reqList) > 1 || qb.currentBatch.req.ItemsCount() >= qb.batchCfg.MinSizeItems { + if len(reqList) > 1 || qb.currentBatch.req.ItemsCount() >= qb.batchCfg.MinSize { firstBatch = qb.currentBatch qb.currentBatch = nil } @@ -137,7 +137,7 @@ func (qb *defaultBatcher) Consume(ctx context.Context, req request.Request, done // If we still have results to process, then we need to check if the last result has enough data to flush, or we add it to the currentBatch. if len(reqList) > 0 { lastReq := reqList[len(reqList)-1] - if lastReq.ItemsCount() < qb.batchCfg.MinSizeItems { + if lastReq.ItemsCount() < qb.batchCfg.MinSize { // Do not flush the last item and add it to the current batch. reqList = reqList[:len(reqList)-1] qb.currentBatch = &batch{ diff --git a/exporter/exporterhelper/internal/batcher/default_batcher_test.go b/exporter/exporterhelper/internal/batcher/default_batcher_test.go index d028c020a11..cfdbd409669 100644 --- a/exporter/exporterhelper/internal/batcher/default_batcher_test.go +++ b/exporter/exporterhelper/internal/batcher/default_batcher_test.go @@ -39,8 +39,9 @@ func TestDefaultBatcher_NoSplit_MinThresholdZero_TimeoutDisabled(t *testing.T) { cfg := exporterbatcher.NewDefaultConfig() cfg.Enabled = true cfg.FlushTimeout = 0 - cfg.MinSizeConfig = exporterbatcher.MinSizeConfig{ - MinSizeItems: 0, + cfg.SizeConfig = exporterbatcher.SizeConfig{ + Sizer: exporterbatcher.SizerTypeItems, + MinSize: 0, } ba, err := NewBatcher(cfg, @@ -89,8 +90,9 @@ func TestDefaultBatcher_NoSplit_TimeoutDisabled(t *testing.T) { cfg := exporterbatcher.NewDefaultConfig() cfg.Enabled = true cfg.FlushTimeout = 0 - cfg.MinSizeConfig = exporterbatcher.MinSizeConfig{ - MinSizeItems: 10, + cfg.SizeConfig = exporterbatcher.SizeConfig{ + Sizer: exporterbatcher.SizerTypeItems, + MinSize: 10, } ba, err := NewBatcher(cfg, @@ -154,8 +156,9 @@ func TestDefaultBatcher_NoSplit_WithTimeout(t *testing.T) { cfg := exporterbatcher.NewDefaultConfig() cfg.Enabled = true cfg.FlushTimeout = 50 * time.Millisecond - cfg.MinSizeConfig = exporterbatcher.MinSizeConfig{ - MinSizeItems: 100, + cfg.SizeConfig = exporterbatcher.SizeConfig{ + Sizer: exporterbatcher.SizerTypeItems, + MinSize: 100, } ba, err := NewBatcher(cfg, @@ -211,11 +214,10 @@ func TestDefaultBatcher_Split_TimeoutDisabled(t *testing.T) { cfg := exporterbatcher.NewDefaultConfig() cfg.Enabled = true cfg.FlushTimeout = 0 - cfg.MinSizeConfig = exporterbatcher.MinSizeConfig{ - MinSizeItems: 100, - } - cfg.MaxSizeConfig = exporterbatcher.MaxSizeConfig{ - MaxSizeItems: 100, + cfg.SizeConfig = exporterbatcher.SizeConfig{ + Sizer: exporterbatcher.SizerTypeItems, + MinSize: 100, + MaxSize: 100, } ba, err := NewBatcher(cfg, @@ -264,7 +266,7 @@ func TestDefaultBatcher_Split_TimeoutDisabled(t *testing.T) { func TestDefaultBatcher_Shutdown(t *testing.T) { batchCfg := exporterbatcher.NewDefaultConfig() - batchCfg.MinSizeItems = 10 + batchCfg.MinSize = 10 batchCfg.FlushTimeout = 100 * time.Second ba, err := NewBatcher(batchCfg, @@ -293,8 +295,8 @@ func TestDefaultBatcher_Shutdown(t *testing.T) { func TestDefaultBatcher_MergeError(t *testing.T) { batchCfg := exporterbatcher.NewDefaultConfig() - batchCfg.MinSizeItems = 5 - batchCfg.MaxSizeItems = 7 + batchCfg.MinSize = 5 + batchCfg.MaxSize = 7 ba, err := NewBatcher(batchCfg, func(ctx context.Context, req request.Request) error { return req.Export(ctx) }, diff --git a/exporter/exporterhelper/internal/oteltest/tracetest.go b/exporter/exporterhelper/internal/oteltest/tracetest.go new file mode 100644 index 00000000000..a05bf302978 --- /dev/null +++ b/exporter/exporterhelper/internal/oteltest/tracetest.go @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package oteltest // import "go.opentelemetry.io/collector/exporter/exporterhelper/internal/oteltest" + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +func CheckStatus(t *testing.T, sd sdktrace.ReadOnlySpan, err error) { + if err != nil { + require.Equal(t, codes.Error, sd.Status().Code) + require.EqualError(t, err, sd.Status().Description) + } else { + require.Equal(t, codes.Unset, sd.Status().Code) + } +} diff --git a/exporter/exporterhelper/internal/queue_sender_test.go b/exporter/exporterhelper/internal/queue_sender_test.go index 927af0ad27d..df48a739bcf 100644 --- a/exporter/exporterhelper/internal/queue_sender_test.go +++ b/exporter/exporterhelper/internal/queue_sender_test.go @@ -285,7 +285,7 @@ func TestQueueBatcher_Merge(t *testing.T) { name: "split_disabled", batchCfg: func() exporterbatcher.Config { cfg := exporterbatcher.NewDefaultConfig() - cfg.MinSizeItems = 10 + cfg.MinSize = 10 cfg.FlushTimeout = 100 * time.Millisecond return cfg }(), @@ -294,9 +294,9 @@ func TestQueueBatcher_Merge(t *testing.T) { name: "split_high_limit", batchCfg: func() exporterbatcher.Config { cfg := exporterbatcher.NewDefaultConfig() - cfg.MinSizeItems = 10 + cfg.MinSize = 10 cfg.FlushTimeout = 100 * time.Millisecond - cfg.MaxSizeItems = 1000 + cfg.MaxSize = 1000 return cfg }(), }, @@ -353,7 +353,7 @@ func TestQueueBatcher_BatchExportError(t *testing.T) { name: "merge_only", batchCfg: func() exporterbatcher.Config { cfg := exporterbatcher.NewDefaultConfig() - cfg.MinSizeItems = 10 + cfg.MinSize = 10 return cfg }(), }, @@ -361,8 +361,8 @@ func TestQueueBatcher_BatchExportError(t *testing.T) { name: "merge_without_split_triggered", batchCfg: func() exporterbatcher.Config { cfg := exporterbatcher.NewDefaultConfig() - cfg.MinSizeItems = 10 - cfg.MaxSizeItems = 200 + cfg.MinSize = 10 + cfg.MaxSize = 200 return cfg }(), }, @@ -370,8 +370,8 @@ func TestQueueBatcher_BatchExportError(t *testing.T) { name: "merge_with_split_triggered", batchCfg: func() exporterbatcher.Config { cfg := exporterbatcher.NewDefaultConfig() - cfg.MinSizeItems = 10 - cfg.MaxSizeItems = 20 + cfg.MinSize = 10 + cfg.MaxSize = 20 return cfg }(), expectedRequests: 1, @@ -410,8 +410,8 @@ func TestQueueBatcher_BatchExportError(t *testing.T) { func TestQueueBatcher_MergeOrSplit(t *testing.T) { batchCfg := exporterbatcher.NewDefaultConfig() - batchCfg.MinSizeItems = 5 - batchCfg.MaxSizeItems = 10 + batchCfg.MinSize = 5 + batchCfg.MaxSize = 10 batchCfg.FlushTimeout = 100 * time.Millisecond be, err := newQueueBatcherExporter(exporterqueue.NewDefaultConfig(), batchCfg) require.NoError(t, err) @@ -446,7 +446,7 @@ func TestQueueBatcher_MergeOrSplit(t *testing.T) { func TestQueueBatcher_Shutdown(t *testing.T) { batchCfg := exporterbatcher.NewDefaultConfig() - batchCfg.MinSizeItems = 10 + batchCfg.MinSize = 10 be, err := newQueueBatcherExporter(exporterqueue.NewDefaultConfig(), batchCfg) require.NoError(t, err) require.NoError(t, be.Start(context.Background(), componenttest.NewNopHost())) @@ -466,7 +466,7 @@ func TestQueueBatcher_Shutdown(t *testing.T) { func TestQueueBatcher_BatchBlocking(t *testing.T) { bCfg := exporterbatcher.NewDefaultConfig() - bCfg.MinSizeItems = 3 + bCfg.MinSize = 3 be, err := newQueueBatcherExporter(exporterqueue.Config{}, bCfg) require.NoError(t, err) require.NoError(t, be.Start(context.Background(), componenttest.NewNopHost())) @@ -493,7 +493,7 @@ func TestQueueBatcher_BatchBlocking(t *testing.T) { // Validate that the batch is cancelled once the first request in the request is cancelled func TestQueueBatcher_BatchCancelled(t *testing.T) { bCfg := exporterbatcher.NewDefaultConfig() - bCfg.MinSizeItems = 2 + bCfg.MinSize = 2 be, err := newQueueBatcherExporter(exporterqueue.Config{}, bCfg) require.NoError(t, err) require.NoError(t, be.Start(context.Background(), componenttest.NewNopHost())) @@ -525,7 +525,7 @@ func TestQueueBatcher_BatchCancelled(t *testing.T) { func TestQueueBatcher_DrainActiveRequests(t *testing.T) { bCfg := exporterbatcher.NewDefaultConfig() - bCfg.MinSizeItems = 2 + bCfg.MinSize = 2 be, err := newQueueBatcherExporter(exporterqueue.Config{}, bCfg) require.NoError(t, err) @@ -564,7 +564,7 @@ func TestQueueBatcherUnstartedShutdown(t *testing.T) { func TestQueueBatcherWithTimeout(t *testing.T) { bCfg := exporterbatcher.NewDefaultConfig() - bCfg.MinSizeItems = 10 + bCfg.MinSize = 10 be, err := newQueueBatcherExporter(exporterqueue.Config{}, bCfg) require.NoError(t, err) @@ -605,7 +605,7 @@ func TestQueueBatcherWithTimeout(t *testing.T) { func TestQueueBatcherTimerResetNoConflict(t *testing.T) { bCfg := exporterbatcher.NewDefaultConfig() - bCfg.MinSizeItems = 8 + bCfg.MinSize = 8 bCfg.FlushTimeout = 100 * time.Millisecond be, err := newQueueBatcherExporter(exporterqueue.Config{}, bCfg) require.NoError(t, err) @@ -635,7 +635,7 @@ func TestQueueBatcherTimerFlush(t *testing.T) { t.Skip("skipping flaky test on Windows, see https://github.com/open-telemetry/opentelemetry-collector/issues/10802") } bCfg := exporterbatcher.NewDefaultConfig() - bCfg.MinSizeItems = 8 + bCfg.MinSize = 8 bCfg.FlushTimeout = 100 * time.Millisecond be, err := newQueueBatcherExporter(exporterqueue.Config{}, bCfg) require.NoError(t, err) diff --git a/exporter/exporterhelper/internal/request/request.go b/exporter/exporterhelper/internal/request/request.go index ab4253a3452..3283f01b6be 100644 --- a/exporter/exporterhelper/internal/request/request.go +++ b/exporter/exporterhelper/internal/request/request.go @@ -28,7 +28,7 @@ type Request interface { // marked as not mutable. The length of the returned slice MUST not be 0. // Experimental: This API is at the early stage of development and may change without backward compatibility // until https://github.com/open-telemetry/opentelemetry-collector/issues/8122 is resolved. - MergeSplit(context.Context, exporterbatcher.MaxSizeConfig, Request) ([]Request, error) + MergeSplit(context.Context, exporterbatcher.SizeConfig, Request) ([]Request, error) } // RequestErrorHandler is an optional interface that can be implemented by Request to provide a way handle partial diff --git a/exporter/exporterhelper/internal/requesttest/fake_request.go b/exporter/exporterhelper/internal/requesttest/fake_request.go index 33d7a648cb0..ae1dd450fa6 100644 --- a/exporter/exporterhelper/internal/requesttest/fake_request.go +++ b/exporter/exporterhelper/internal/requesttest/fake_request.go @@ -98,12 +98,12 @@ func (r *FakeRequest) ItemsCount() int { return r.Items } -func (r *FakeRequest) MergeSplit(_ context.Context, cfg exporterbatcher.MaxSizeConfig, r2 request.Request) ([]request.Request, error) { +func (r *FakeRequest) MergeSplit(_ context.Context, cfg exporterbatcher.SizeConfig, r2 request.Request) ([]request.Request, error) { if r.MergeErr != nil { return nil, r.MergeErr } - maxItems := cfg.MaxSizeItems + maxItems := cfg.MaxSize if maxItems == 0 { if r2 != nil { fr2 := r2.(*FakeRequest) diff --git a/exporter/exporterhelper/internal/retry_sender_test.go b/exporter/exporterhelper/internal/retry_sender_test.go index 2a3f753e61f..20a404485f0 100644 --- a/exporter/exporterhelper/internal/retry_sender_test.go +++ b/exporter/exporterhelper/internal/retry_sender_test.go @@ -165,7 +165,7 @@ func (mer *mockErrorRequest) ItemsCount() int { return 7 } -func (mer *mockErrorRequest) MergeSplit(context.Context, exporterbatcher.MaxSizeConfig, request.Request) ([]request.Request, error) { +func (mer *mockErrorRequest) MergeSplit(context.Context, exporterbatcher.SizeConfig, request.Request) ([]request.Request, error) { return nil, nil } diff --git a/exporter/exporterhelper/internal/sizer/logs_sizer.go b/exporter/exporterhelper/internal/sizer/logs_sizer.go new file mode 100644 index 00000000000..bfd380b634e --- /dev/null +++ b/exporter/exporterhelper/internal/sizer/logs_sizer.go @@ -0,0 +1,68 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package sizer // import "go.opentelemetry.io/collector/exporter/exporterhelper/internal/sizer" + +import ( + math_bits "math/bits" + + "go.opentelemetry.io/collector/pdata/plog" +) + +type LogsSizer interface { + LogsSize(ld plog.Logs) int + ResourceLogsSize(rl plog.ResourceLogs) int + ScopeLogsSize(sl plog.ScopeLogs) int + LogRecordSize(lr plog.LogRecord) int + + // DeltaSize returns the delta size when a ResourceLog, ScopeLog or LogRecord is added. + DeltaSize(newItemSize int) int +} + +// LogsBytesSizer returns the byte size of serialized protos. +type LogsBytesSizer struct { + plog.ProtoMarshaler +} + +// DeltaSize returns the delta size of a proto slice when a new item is added. +// Example: +// +// prevSize := proto1.Size() +// proto1.RepeatedField().AppendEmpty() = proto2 +// +// Then currSize of proto1 can be calculated as +// +// currSize := (prevSize + sizer.DeltaSize(proto2.Size())) +// +// This is derived from opentelemetry-collector/pdata/internal/data/protogen/logs/v1/logs.pb.go +// which is generated with gogo/protobuf. +func (s *LogsBytesSizer) DeltaSize(newItemSize int) int { + return 1 + newItemSize + math_bits.Len64(uint64(newItemSize|1)+6)/7 //nolint:gosec // disable G115 +} + +// LogsCountSizer returns the nunmber of logs entries. +type LogsCountSizer struct{} + +func (s *LogsCountSizer) LogsSize(ld plog.Logs) int { + return ld.LogRecordCount() +} + +func (s *LogsCountSizer) ResourceLogsSize(rl plog.ResourceLogs) int { + count := 0 + for k := 0; k < rl.ScopeLogs().Len(); k++ { + count += rl.ScopeLogs().At(k).LogRecords().Len() + } + return count +} + +func (s *LogsCountSizer) ScopeLogsSize(sl plog.ScopeLogs) int { + return sl.LogRecords().Len() +} + +func (s *LogsCountSizer) LogRecordSize(_ plog.LogRecord) int { + return 1 +} + +func (s *LogsCountSizer) DeltaSize(newItemSize int) int { + return newItemSize +} diff --git a/exporter/exporterhelper/internal/sizer/logs_sizer_test.go b/exporter/exporterhelper/internal/sizer/logs_sizer_test.go new file mode 100644 index 00000000000..196d1655ede --- /dev/null +++ b/exporter/exporterhelper/internal/sizer/logs_sizer_test.go @@ -0,0 +1,57 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +package sizer // import "go.opentelemetry.io/collector/exporter/exporterhelper/internal/sizer" + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/pdata/testdata" +) + +func TestLogsCountSizer(t *testing.T) { + ld := testdata.GenerateLogs(5) + sizer := LogsCountSizer{} + require.Equal(t, 5, sizer.LogsSize(ld)) + + rl := ld.ResourceLogs().At(0) + require.Equal(t, 5, sizer.ResourceLogsSize(rl)) + + sl := rl.ScopeLogs().At(0) + require.Equal(t, 5, sizer.ScopeLogsSize(sl)) + + require.Equal(t, 1, sizer.LogRecordSize(sl.LogRecords().At(0))) + require.Equal(t, 1, sizer.LogRecordSize(sl.LogRecords().At(1))) + require.Equal(t, 1, sizer.LogRecordSize(sl.LogRecords().At(2))) + require.Equal(t, 1, sizer.LogRecordSize(sl.LogRecords().At(3))) + require.Equal(t, 1, sizer.LogRecordSize(sl.LogRecords().At(4))) + + prevSize := sizer.ScopeLogsSize(sl) + lr := sl.LogRecords().At(2) + lr.CopyTo(sl.LogRecords().AppendEmpty()) + require.Equal(t, sizer.ScopeLogsSize(sl), prevSize+sizer.DeltaSize(sizer.LogRecordSize(lr))) +} + +func TestLogsBytesSizer(t *testing.T) { + ld := testdata.GenerateLogs(5) + sizer := LogsBytesSizer{} + require.Equal(t, 545, sizer.LogsSize(ld)) + + rl := ld.ResourceLogs().At(0) + require.Equal(t, 542, sizer.ResourceLogsSize(rl)) + + sl := rl.ScopeLogs().At(0) + require.Equal(t, 497, sizer.ScopeLogsSize(sl)) + + require.Equal(t, 109, sizer.LogRecordSize(sl.LogRecords().At(0))) + require.Equal(t, 79, sizer.LogRecordSize(sl.LogRecords().At(1))) + require.Equal(t, 109, sizer.LogRecordSize(sl.LogRecords().At(2))) + require.Equal(t, 79, sizer.LogRecordSize(sl.LogRecords().At(3))) + require.Equal(t, 109, sizer.LogRecordSize(sl.LogRecords().At(4))) + + prevSize := sizer.ScopeLogsSize(sl) + lr := sl.LogRecords().At(2) + lr.CopyTo(sl.LogRecords().AppendEmpty()) + require.Equal(t, sizer.ScopeLogsSize(sl), prevSize+sizer.DeltaSize(sizer.LogRecordSize(lr))) +} diff --git a/exporter/exporterhelper/logs.go b/exporter/exporterhelper/logs.go index 8ca1e55790e..1fb5244bc15 100644 --- a/exporter/exporterhelper/logs.go +++ b/exporter/exporterhelper/logs.go @@ -14,6 +14,7 @@ import ( "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper/internal" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/sizer" "go.opentelemetry.io/collector/exporter/exporterqueue" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pipeline" @@ -25,16 +26,16 @@ var ( ) type logsRequest struct { - ld plog.Logs - pusher consumer.ConsumeLogsFunc - cachedItemsCount int + ld plog.Logs + pusher consumer.ConsumeLogsFunc + cachedSize int } func newLogsRequest(ld plog.Logs, pusher consumer.ConsumeLogsFunc) Request { return &logsRequest{ - ld: ld, - pusher: pusher, - cachedItemsCount: ld.LogRecordCount(), + ld: ld, + pusher: pusher, + cachedSize: -1, } } @@ -65,11 +66,18 @@ func (req *logsRequest) Export(ctx context.Context) error { } func (req *logsRequest) ItemsCount() int { - return req.cachedItemsCount + return req.ld.LogRecordCount() } -func (req *logsRequest) setCachedItemsCount(count int) { - req.cachedItemsCount = count +func (req *logsRequest) Size(sizer sizer.LogsSizer) int { + if req.cachedSize == -1 { + req.cachedSize = sizer.LogsSize(req.ld) + } + return req.cachedSize +} + +func (req *logsRequest) setCachedSize(size int) { + req.cachedSize = size } type logsExporter struct { @@ -131,19 +139,23 @@ func NewLogsRequest( return nil, err } - lc, err := consumer.NewLogs(func(ctx context.Context, ld plog.Logs) error { - req, cErr := converter(ctx, ld) - if cErr != nil { - set.Logger.Error("Failed to convert logs. Dropping data.", + lc, err := consumer.NewLogs(newConsumeLogs(converter, be, set.Logger), be.ConsumerOptions...) + if err != nil { + return nil, err + } + + return &logsExporter{BaseExporter: be, Logs: lc}, nil +} + +func newConsumeLogs(converter RequestFromLogsFunc, be *internal.BaseExporter, logger *zap.Logger) consumer.ConsumeLogsFunc { + return func(ctx context.Context, ld plog.Logs) error { + req, err := converter(ctx, ld) + if err != nil { + logger.Error("Failed to convert metrics. Dropping data.", zap.Int("dropped_log_records", ld.LogRecordCount()), zap.Error(err)) - return consumererror.NewPermanent(cErr) + return consumererror.NewPermanent(err) } return be.Send(ctx, req) - }, be.ConsumerOptions...) - - return &logsExporter{ - BaseExporter: be, - Logs: lc, - }, err + } } diff --git a/exporter/exporterhelper/logs_batch.go b/exporter/exporterhelper/logs_batch.go index 3618182d466..4849c8eba9c 100644 --- a/exporter/exporterhelper/logs_batch.go +++ b/exporter/exporterhelper/logs_batch.go @@ -8,57 +8,78 @@ import ( "errors" "go.opentelemetry.io/collector/exporter/exporterbatcher" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/sizer" "go.opentelemetry.io/collector/pdata/plog" ) +func sizerFromConfig(cfg exporterbatcher.SizeConfig) sizer.LogsSizer { + switch cfg.Sizer { + case exporterbatcher.SizerTypeItems: + return &sizer.LogsCountSizer{} + case exporterbatcher.SizerTypeBytes: + return &sizer.LogsBytesSizer{} + default: + return &sizer.LogsCountSizer{} + } +} + // MergeSplit splits and/or merges the provided logs request and the current request into one or more requests // conforming with the MaxSizeConfig. -func (req *logsRequest) MergeSplit(_ context.Context, cfg exporterbatcher.MaxSizeConfig, r2 Request) ([]Request, error) { +func (req *logsRequest) MergeSplit(_ context.Context, cfg exporterbatcher.SizeConfig, r2 Request) ([]Request, error) { + sizer := sizerFromConfig(cfg) if r2 != nil { req2, ok := r2.(*logsRequest) if !ok { return nil, errors.New("invalid input type") } - req2.mergeTo(req) + req2.mergeTo(req, sizer) } // If no limit we can simply merge the new request into the current and return. - if cfg.MaxSizeItems == 0 { + if cfg.MaxSize == 0 { return []Request{req}, nil } - return req.split(cfg) + + return req.split(cfg.MaxSize, sizer), nil } -func (req *logsRequest) mergeTo(dst *logsRequest) { - dst.setCachedItemsCount(dst.ItemsCount() + req.ItemsCount()) - req.setCachedItemsCount(0) +func (req *logsRequest) mergeTo(dst *logsRequest, sizer sizer.LogsSizer) { + if sizer != nil { + dst.setCachedSize(dst.Size(sizer) + req.Size(sizer)) + req.setCachedSize(0) + } req.ld.ResourceLogs().MoveAndAppendTo(dst.ld.ResourceLogs()) } -func (req *logsRequest) split(cfg exporterbatcher.MaxSizeConfig) ([]Request, error) { +func (req *logsRequest) split(maxSize int, sizer sizer.LogsSizer) []Request { var res []Request - for req.ItemsCount() > cfg.MaxSizeItems { - ld := extractLogs(req.ld, cfg.MaxSizeItems) - size := ld.LogRecordCount() - req.setCachedItemsCount(req.ItemsCount() - size) - res = append(res, &logsRequest{ld: ld, pusher: req.pusher, cachedItemsCount: size}) + for req.Size(sizer) > maxSize { + ld := extractLogs(req.ld, maxSize, sizer) + size := sizer.LogsSize(ld) + req.setCachedSize(req.Size(sizer) - size) + res = append(res, &logsRequest{ld: ld, pusher: req.pusher, cachedSize: size}) } res = append(res, req) - return res, nil + return res } // extractLogs extracts logs from the input logs and returns a new logs with the specified number of log records. -func extractLogs(srcLogs plog.Logs, count int) plog.Logs { +func extractLogs(srcLogs plog.Logs, capacity int, sizer sizer.LogsSizer) plog.Logs { + capacityReached := false destLogs := plog.NewLogs() + capacityLeft := capacity - sizer.LogsSize(destLogs) srcLogs.ResourceLogs().RemoveIf(func(srcRL plog.ResourceLogs) bool { - if count == 0 { + if capacityReached { return false } - needToExtract := resourceLogsCount(srcRL) > count + needToExtract := sizer.ResourceLogsSize(srcRL) > capacityLeft if needToExtract { - srcRL = extractResourceLogs(srcRL, count) + srcRL, capacityReached = extractResourceLogs(srcRL, capacityLeft, sizer) + if srcRL.ScopeLogs().Len() == 0 { + return false + } } - count -= resourceLogsCount(srcRL) + capacityLeft -= sizer.DeltaSize(sizer.ResourceLogsSize(srcRL)) srcRL.MoveTo(destLogs.ResourceLogs().AppendEmpty()) return !needToExtract }) @@ -66,46 +87,45 @@ func extractLogs(srcLogs plog.Logs, count int) plog.Logs { } // extractResourceLogs extracts resource logs and returns a new resource logs with the specified number of log records. -func extractResourceLogs(srcRL plog.ResourceLogs, count int) plog.ResourceLogs { +func extractResourceLogs(srcRL plog.ResourceLogs, capacity int, sizer sizer.LogsSizer) (plog.ResourceLogs, bool) { + capacityReached := false destRL := plog.NewResourceLogs() destRL.SetSchemaUrl(srcRL.SchemaUrl()) srcRL.Resource().CopyTo(destRL.Resource()) + capacityLeft := capacity - sizer.ResourceLogsSize(destRL) srcRL.ScopeLogs().RemoveIf(func(srcSL plog.ScopeLogs) bool { - if count == 0 { + if capacityReached { return false } - needToExtract := srcSL.LogRecords().Len() > count + needToExtract := sizer.ScopeLogsSize(srcSL) > capacityLeft if needToExtract { - srcSL = extractScopeLogs(srcSL, count) + srcSL, capacityReached = extractScopeLogs(srcSL, capacityLeft, sizer) + if srcSL.LogRecords().Len() == 0 { + return false + } } - count -= srcSL.LogRecords().Len() + capacityLeft -= sizer.DeltaSize(sizer.ScopeLogsSize(srcSL)) srcSL.MoveTo(destRL.ScopeLogs().AppendEmpty()) return !needToExtract }) - return destRL + return destRL, capacityReached } // extractScopeLogs extracts scope logs and returns a new scope logs with the specified number of log records. -func extractScopeLogs(srcSL plog.ScopeLogs, count int) plog.ScopeLogs { +func extractScopeLogs(srcSL plog.ScopeLogs, capacity int, sizer sizer.LogsSizer) (plog.ScopeLogs, bool) { + capacityReached := false destSL := plog.NewScopeLogs() destSL.SetSchemaUrl(srcSL.SchemaUrl()) srcSL.Scope().CopyTo(destSL.Scope()) + capacityLeft := capacity - sizer.ScopeLogsSize(destSL) srcSL.LogRecords().RemoveIf(func(srcLR plog.LogRecord) bool { - if count == 0 { + if capacityReached || sizer.LogRecordSize(srcLR) > capacityLeft { + capacityReached = true return false } + capacityLeft -= sizer.DeltaSize(sizer.LogRecordSize(srcLR)) srcLR.MoveTo(destSL.LogRecords().AppendEmpty()) - count-- return true }) - return destSL -} - -// resourceLogsCount calculates the total number of log records in the plog.ResourceLogs. -func resourceLogsCount(rl plog.ResourceLogs) int { - count := 0 - for k := 0; k < rl.ScopeLogs().Len(); k++ { - count += rl.ScopeLogs().At(k).LogRecords().Len() - } - return count + return destSL, capacityReached } diff --git a/exporter/exporterhelper/logs_batch_test.go b/exporter/exporterhelper/logs_batch_test.go index c2267f0a516..635faa54209 100644 --- a/exporter/exporterhelper/logs_batch_test.go +++ b/exporter/exporterhelper/logs_batch_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/exporter/exporterbatcher" - "go.opentelemetry.io/collector/exporter/exporterhelper/internal/request" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/sizer" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/testdata" ) @@ -19,7 +19,7 @@ import ( func TestMergeLogs(t *testing.T) { lr1 := newLogsRequest(testdata.GenerateLogs(2), nil) lr2 := newLogsRequest(testdata.GenerateLogs(3), nil) - res, err := lr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, lr2) + res, err := lr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, lr2) require.NoError(t, err) require.Equal(t, 5, res[0].ItemsCount()) } @@ -27,42 +27,42 @@ func TestMergeLogs(t *testing.T) { func TestMergeLogsInvalidInput(t *testing.T) { lr1 := newTracesRequest(testdata.GenerateTraces(2), nil) lr2 := newLogsRequest(testdata.GenerateLogs(3), nil) - _, err := lr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, lr2) + _, err := lr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, lr2) require.Error(t, err) } func TestMergeSplitLogs(t *testing.T) { tests := []struct { name string - cfg exporterbatcher.MaxSizeConfig - lr1 request.Request - lr2 request.Request + cfg exporterbatcher.SizeConfig + lr1 Request + lr2 Request expected []Request }{ { name: "both_requests_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, lr1: newLogsRequest(plog.NewLogs(), nil), lr2: newLogsRequest(plog.NewLogs(), nil), expected: []Request{newLogsRequest(plog.NewLogs(), nil)}, }, { name: "first_request_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, lr1: newLogsRequest(plog.NewLogs(), nil), lr2: newLogsRequest(testdata.GenerateLogs(5), nil), expected: []Request{newLogsRequest(testdata.GenerateLogs(5), nil)}, }, { name: "first_empty_second_nil", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, lr1: newLogsRequest(plog.NewLogs(), nil), lr2: nil, expected: []Request{newLogsRequest(plog.NewLogs(), nil)}, }, { name: "merge_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, lr1: newLogsRequest(testdata.GenerateLogs(4), nil), lr2: newLogsRequest(testdata.GenerateLogs(6), nil), expected: []Request{newLogsRequest(func() plog.Logs { @@ -73,7 +73,7 @@ func TestMergeSplitLogs(t *testing.T) { }, { name: "split_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 4}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 4}, lr1: newLogsRequest(plog.NewLogs(), nil), lr2: newLogsRequest(testdata.GenerateLogs(10), nil), expected: []Request{ @@ -84,7 +84,7 @@ func TestMergeSplitLogs(t *testing.T) { }, { name: "merge_and_split", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, lr1: newLogsRequest(testdata.GenerateLogs(8), nil), lr2: newLogsRequest(testdata.GenerateLogs(20), nil), expected: []Request{ @@ -99,7 +99,7 @@ func TestMergeSplitLogs(t *testing.T) { }, { name: "scope_logs_split", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 4}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 4}, lr1: newLogsRequest(func() plog.Logs { ld := testdata.GenerateLogs(4) ld.ResourceLogs().At(0).ScopeLogs().AppendEmpty().LogRecords().AppendEmpty().Body().SetStr("extra log") @@ -123,7 +123,108 @@ func TestMergeSplitLogs(t *testing.T) { require.NoError(t, err) assert.Equal(t, len(tt.expected), len(res)) for i := range res { - assert.Equal(t, tt.expected[i], res[i]) + assert.Equal(t, tt.expected[i].(*logsRequest).ld, res[i].(*logsRequest).ld) + } + }) + } +} + +func TestMergeSplitLogsBasedOnByteSize(t *testing.T) { + tests := []struct { + name string + cfg exporterbatcher.SizeConfig + lr1 Request + lr2 Request + expected []Request + }{ + { + name: "both_requests_empty", + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(10))}, + lr1: newLogsRequest(plog.NewLogs(), nil), + lr2: newLogsRequest(plog.NewLogs(), nil), + expected: []Request{newLogsRequest(plog.NewLogs(), nil)}, + }, + { + name: "first_request_empty", + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(10))}, + lr1: newLogsRequest(plog.NewLogs(), nil), + lr2: newLogsRequest(testdata.GenerateLogs(5), nil), + expected: []Request{newLogsRequest(testdata.GenerateLogs(5), nil)}, + }, + { + name: "first_empty_second_nil", + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(10))}, + lr1: newLogsRequest(plog.NewLogs(), nil), + lr2: nil, + expected: []Request{newLogsRequest(plog.NewLogs(), nil)}, + }, + { + name: "merge_only", + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(11))}, + lr1: newLogsRequest(testdata.GenerateLogs(4), nil), + lr2: newLogsRequest(testdata.GenerateLogs(6), nil), + expected: []Request{newLogsRequest(func() plog.Logs { + logs := testdata.GenerateLogs(4) + testdata.GenerateLogs(6).ResourceLogs().MoveAndAppendTo(logs.ResourceLogs()) + return logs + }(), nil)}, + }, + { + name: "split_only", + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(4))}, + lr1: newLogsRequest(plog.NewLogs(), nil), + lr2: newLogsRequest(testdata.GenerateLogs(10), nil), + expected: []Request{ + newLogsRequest(testdata.GenerateLogs(4), nil), + newLogsRequest(testdata.GenerateLogs(4), nil), + newLogsRequest(testdata.GenerateLogs(2), nil), + }, + }, + { + name: "merge_and_split", + cfg: exporterbatcher.SizeConfig{ + Sizer: exporterbatcher.SizerTypeBytes, + MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(10))/2 + logsMarshaler.LogsSize(testdata.GenerateLogs(11))/2, + }, + lr1: newLogsRequest(testdata.GenerateLogs(8), nil), + lr2: newLogsRequest(testdata.GenerateLogs(20), nil), + expected: []Request{ + newLogsRequest(func() plog.Logs { + logs := testdata.GenerateLogs(8) + testdata.GenerateLogs(2).ResourceLogs().MoveAndAppendTo(logs.ResourceLogs()) + return logs + }(), nil), + newLogsRequest(testdata.GenerateLogs(10), nil), + newLogsRequest(testdata.GenerateLogs(8), nil), + }, + }, + { + name: "scope_logs_split", + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(4))}, + lr1: newLogsRequest(func() plog.Logs { + ld := testdata.GenerateLogs(4) + ld.ResourceLogs().At(0).ScopeLogs().AppendEmpty().LogRecords().AppendEmpty().Body().SetStr("extra log") + return ld + }(), nil), + lr2: newLogsRequest(testdata.GenerateLogs(2), nil), + expected: []Request{ + newLogsRequest(testdata.GenerateLogs(4), nil), + newLogsRequest(func() plog.Logs { + ld := testdata.GenerateLogs(0) + ld.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().AppendEmpty().Body().SetStr("extra log") + testdata.GenerateLogs(2).ResourceLogs().MoveAndAppendTo(ld.ResourceLogs()) + return ld + }(), nil), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, err := tt.lr1.MergeSplit(context.Background(), tt.cfg, tt.lr2) + require.NoError(t, err) + assert.Equal(t, len(tt.expected), len(res)) + for i := range res { + assert.Equal(t, tt.expected[i].(*logsRequest).ld, res[i].(*logsRequest).ld) } }) } @@ -132,7 +233,7 @@ func TestMergeSplitLogs(t *testing.T) { func TestMergeSplitLogsInputNotModifiedIfErrorReturned(t *testing.T) { r1 := newLogsRequest(testdata.GenerateLogs(18), nil) r2 := newTracesRequest(testdata.GenerateTraces(3), nil) - _, err := r1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, r2) + _, err := r1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, r2) require.Error(t, err) assert.Equal(t, 18, r1.ItemsCount()) } @@ -140,14 +241,14 @@ func TestMergeSplitLogsInputNotModifiedIfErrorReturned(t *testing.T) { func TestMergeSplitLogsInvalidInput(t *testing.T) { r1 := newTracesRequest(testdata.GenerateTraces(2), nil) r2 := newLogsRequest(testdata.GenerateLogs(3), nil) - _, err := r1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, r2) + _, err := r1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{}, r2) require.Error(t, err) } func TestExtractLogs(t *testing.T) { for i := 0; i < 10; i++ { ld := testdata.GenerateLogs(10) - extractedLogs := extractLogs(ld, i) + extractedLogs := extractLogs(ld, i, &sizer.LogsCountSizer{}) assert.Equal(t, i, extractedLogs.LogRecordCount()) assert.Equal(t, 10-i, ld.LogRecordCount()) } @@ -155,7 +256,7 @@ func TestExtractLogs(t *testing.T) { func TestMergeSplitManySmallLogs(t *testing.T) { // All requests merge into a single batch. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} merged := []Request{newLogsRequest(testdata.GenerateLogs(1), nil)} for j := 0; j < 1000; j++ { lr2 := newLogsRequest(testdata.GenerateLogs(10), nil) @@ -167,7 +268,22 @@ func TestMergeSplitManySmallLogs(t *testing.T) { func BenchmarkSplittingBasedOnItemCountManySmallLogs(b *testing.B) { // All requests merge into a single batch. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10010} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10010} + b.ReportAllocs() + for i := 0; i < b.N; i++ { + merged := []Request{newLogsRequest(testdata.GenerateLogs(10), nil)} + for j := 0; j < 1000; j++ { + lr2 := newLogsRequest(testdata.GenerateLogs(10), nil) + res, _ := merged[len(merged)-1].MergeSplit(context.Background(), cfg, lr2) + merged = append(merged[0:len(merged)-1], res...) + } + assert.Len(b, merged, 1) + } +} + +func BenchmarkSplittingBasedOnByteSizeManySmallLogs(b *testing.B) { + // All requests merge into a single batch. + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(11000))} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newLogsRequest(testdata.GenerateLogs(10), nil)} @@ -182,13 +298,29 @@ func BenchmarkSplittingBasedOnItemCountManySmallLogs(b *testing.B) { func BenchmarkSplittingBasedOnItemCountManyLogsSlightlyAboveLimit(b *testing.B) { // Every incoming request results in a split. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} + b.ReportAllocs() + for i := 0; i < b.N; i++ { + merged := []Request{newLogsRequest(testdata.GenerateLogs(0), nil)} + for j := 0; j < 10; j++ { + lr2 := newLogsRequest(testdata.GenerateLogs(10001), nil) + res, _ := merged[len(merged)-1].MergeSplit(context.Background(), cfg, lr2) + merged = append(merged[0:len(merged)-1], res...) + } + assert.Len(b, merged, 11) + } +} + +func BenchmarkSplittingBasedOnByteSizeManyLogsSlightlyAboveLimit(b *testing.B) { + // Every incoming request results in a split. + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(10000))} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newLogsRequest(testdata.GenerateLogs(0), nil)} for j := 0; j < 10; j++ { lr2 := newLogsRequest(testdata.GenerateLogs(10001), nil) res, _ := merged[len(merged)-1].MergeSplit(context.Background(), cfg, lr2) + assert.Len(b, res, 2) merged = append(merged[0:len(merged)-1], res...) } assert.Len(b, merged, 11) @@ -197,7 +329,20 @@ func BenchmarkSplittingBasedOnItemCountManyLogsSlightlyAboveLimit(b *testing.B) func BenchmarkSplittingBasedOnItemCountHugeLogs(b *testing.B) { // One request splits into many batches. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} + b.ReportAllocs() + for i := 0; i < b.N; i++ { + merged := []Request{newLogsRequest(testdata.GenerateLogs(0), nil)} + lr2 := newLogsRequest(testdata.GenerateLogs(100000), nil) + res, _ := merged[len(merged)-1].MergeSplit(context.Background(), cfg, lr2) + merged = append(merged[0:len(merged)-1], res...) + assert.Len(b, merged, 10) + } +} + +func BenchmarkSplittingBasedOnByteSizeHugeLogs(b *testing.B) { + // One request splits into many batches. + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeBytes, MaxSize: logsMarshaler.LogsSize(testdata.GenerateLogs(10010))} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newLogsRequest(testdata.GenerateLogs(0), nil)} diff --git a/exporter/exporterhelper/logs_test.go b/exporter/exporterhelper/logs_test.go index 6a873639257..a9ee80fe47c 100644 --- a/exporter/exporterhelper/logs_test.go +++ b/exporter/exporterhelper/logs_test.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper/internal" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/metadatatest" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/oteltest" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/requesttest" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/exporter/internal/storagetest" @@ -421,7 +422,7 @@ func checkWrapSpanForLogs(t *testing.T, sr *tracetest.SpanRecorder, tracer trace require.Equalf(t, fakeLogsParentSpanName, parentSpan.Name(), "SpanData %v", parentSpan) for _, sd := range gotSpanData[:numRequests] { require.Equalf(t, parentSpan.SpanContext(), sd.Parent(), "Exporter span not a child\nSpanData %v", sd) - internal.CheckStatus(t, sd, wantError) + oteltest.CheckStatus(t, sd, wantError) sentLogRecords := int64(1) failedToSendLogRecords := int64(0) diff --git a/exporter/exporterhelper/metrics.go b/exporter/exporterhelper/metrics.go index 0f414706fed..cc4b090e9fe 100644 --- a/exporter/exporterhelper/metrics.go +++ b/exporter/exporterhelper/metrics.go @@ -131,19 +131,23 @@ func NewMetricsRequest( return nil, err } - mc, err := consumer.NewMetrics(func(ctx context.Context, md pmetric.Metrics) error { - req, cErr := converter(ctx, md) - if cErr != nil { - set.Logger.Error("Failed to convert metrics. Dropping data.", + mc, err := consumer.NewMetrics(newConsumeMetrics(converter, be, set.Logger), be.ConsumerOptions...) + if err != nil { + return nil, err + } + + return &metricsExporter{BaseExporter: be, Metrics: mc}, nil +} + +func newConsumeMetrics(converter RequestFromMetricsFunc, be *internal.BaseExporter, logger *zap.Logger) consumer.ConsumeMetricsFunc { + return func(ctx context.Context, md pmetric.Metrics) error { + req, err := converter(ctx, md) + if err != nil { + logger.Error("Failed to convert metrics. Dropping data.", zap.Int("dropped_data_points", md.DataPointCount()), zap.Error(err)) - return consumererror.NewPermanent(cErr) + return consumererror.NewPermanent(err) } return be.Send(ctx, req) - }, be.ConsumerOptions...) - - return &metricsExporter{ - BaseExporter: be, - Metrics: mc, - }, err + } } diff --git a/exporter/exporterhelper/metrics_batch.go b/exporter/exporterhelper/metrics_batch.go index 390517c6943..8ded9fa36de 100644 --- a/exporter/exporterhelper/metrics_batch.go +++ b/exporter/exporterhelper/metrics_batch.go @@ -13,7 +13,7 @@ import ( // MergeSplit splits and/or merges the provided metrics request and the current request into one or more requests // conforming with the MaxSizeConfig. -func (req *metricsRequest) MergeSplit(_ context.Context, cfg exporterbatcher.MaxSizeConfig, r2 Request) ([]Request, error) { +func (req *metricsRequest) MergeSplit(_ context.Context, cfg exporterbatcher.SizeConfig, r2 Request) ([]Request, error) { if r2 != nil { req2, ok := r2.(*metricsRequest) if !ok { @@ -23,7 +23,7 @@ func (req *metricsRequest) MergeSplit(_ context.Context, cfg exporterbatcher.Max } // If no limit we can simply merge the new request into the current and return. - if cfg.MaxSizeItems == 0 { + if cfg.MaxSize == 0 { return []Request{req}, nil } return req.split(cfg) @@ -35,10 +35,10 @@ func (req *metricsRequest) mergeTo(dst *metricsRequest) { req.md.ResourceMetrics().MoveAndAppendTo(dst.md.ResourceMetrics()) } -func (req *metricsRequest) split(cfg exporterbatcher.MaxSizeConfig) ([]Request, error) { +func (req *metricsRequest) split(cfg exporterbatcher.SizeConfig) ([]Request, error) { var res []Request - for req.ItemsCount() > cfg.MaxSizeItems { - md := extractMetrics(req.md, cfg.MaxSizeItems) + for req.ItemsCount() > cfg.MaxSize { + md := extractMetrics(req.md, cfg.MaxSize) size := md.DataPointCount() req.setCachedItemsCount(req.ItemsCount() - size) res = append(res, &metricsRequest{md: md, pusher: req.pusher, cachedItemsCount: size}) diff --git a/exporter/exporterhelper/metrics_batch_test.go b/exporter/exporterhelper/metrics_batch_test.go index 5969e0f75c8..385d125d983 100644 --- a/exporter/exporterhelper/metrics_batch_test.go +++ b/exporter/exporterhelper/metrics_batch_test.go @@ -18,7 +18,7 @@ import ( func TestMergeMetrics(t *testing.T) { mr1 := newMetricsRequest(testdata.GenerateMetrics(2), nil) mr2 := newMetricsRequest(testdata.GenerateMetrics(3), nil) - res, err := mr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, mr2) + res, err := mr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, mr2) require.NoError(t, err) // Every metric has 2 data points. assert.Equal(t, 2*5, res[0].ItemsCount()) @@ -27,42 +27,42 @@ func TestMergeMetrics(t *testing.T) { func TestMergeMetricsInvalidInput(t *testing.T) { mr1 := newTracesRequest(testdata.GenerateTraces(2), nil) mr2 := newMetricsRequest(testdata.GenerateMetrics(3), nil) - _, err := mr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, mr2) + _, err := mr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, mr2) require.Error(t, err) } func TestMergeSplitMetrics(t *testing.T) { tests := []struct { name string - cfg exporterbatcher.MaxSizeConfig + cfg exporterbatcher.SizeConfig mr1 Request mr2 Request expected []Request }{ { name: "both_requests_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, mr1: newMetricsRequest(pmetric.NewMetrics(), nil), mr2: newMetricsRequest(pmetric.NewMetrics(), nil), expected: []Request{newMetricsRequest(pmetric.NewMetrics(), nil)}, }, { name: "first_request_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, mr1: newMetricsRequest(pmetric.NewMetrics(), nil), mr2: newMetricsRequest(testdata.GenerateMetrics(5), nil), expected: []Request{newMetricsRequest(testdata.GenerateMetrics(5), nil)}, }, { name: "first_empty_second_nil", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, mr1: newMetricsRequest(pmetric.NewMetrics(), nil), mr2: nil, expected: []Request{newMetricsRequest(pmetric.NewMetrics(), nil)}, }, { name: "merge_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 60}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 60}, mr1: newMetricsRequest(testdata.GenerateMetrics(10), nil), mr2: newMetricsRequest(testdata.GenerateMetrics(14), nil), expected: []Request{newMetricsRequest(func() pmetric.Metrics { @@ -73,7 +73,7 @@ func TestMergeSplitMetrics(t *testing.T) { }, { name: "split_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 14}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 14}, mr1: newMetricsRequest(pmetric.NewMetrics(), nil), mr2: newMetricsRequest(testdata.GenerateMetrics(15), nil), // 15 metrics, 30 data points expected: []Request{ @@ -84,7 +84,7 @@ func TestMergeSplitMetrics(t *testing.T) { }, { name: "split_and_merge", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 28}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 28}, mr1: newMetricsRequest(testdata.GenerateMetrics(7), nil), // 7 metrics, 14 data points mr2: newMetricsRequest(testdata.GenerateMetrics(25), nil), // 25 metrics, 50 data points expected: []Request{ @@ -99,7 +99,7 @@ func TestMergeSplitMetrics(t *testing.T) { }, { name: "scope_metrics_split", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 8}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 8}, mr1: newMetricsRequest(func() pmetric.Metrics { md := testdata.GenerateMetrics(4) extraScopeMetrics := md.ResourceMetrics().At(0).ScopeMetrics().AppendEmpty() @@ -133,7 +133,7 @@ func TestMergeSplitMetrics(t *testing.T) { func TestMergeSplitMetricsInputNotModifiedIfErrorReturned(t *testing.T) { r1 := newMetricsRequest(testdata.GenerateMetrics(18), nil) // 18 metrics, 36 data points r2 := newLogsRequest(testdata.GenerateLogs(3), nil) - _, err := r1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, r2) + _, err := r1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, r2) require.Error(t, err) assert.Equal(t, 36, r1.ItemsCount()) } @@ -141,7 +141,7 @@ func TestMergeSplitMetricsInputNotModifiedIfErrorReturned(t *testing.T) { func TestMergeSplitMetricsInvalidInput(t *testing.T) { r1 := newTracesRequest(testdata.GenerateTraces(2), nil) r2 := newMetricsRequest(testdata.GenerateMetrics(3), nil) - _, err := r1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, r2) + _, err := r1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, r2) require.Error(t, err) } @@ -163,7 +163,7 @@ func TestExtractMetricsInvalidMetric(t *testing.T) { func TestMergeSplitManySmallMetrics(t *testing.T) { // All requests merge into a single batch. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 20000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 20000} merged := []Request{newMetricsRequest(testdata.GenerateMetrics(1), nil)} for j := 0; j < 1000; j++ { lr2 := newMetricsRequest(testdata.GenerateMetrics(10), nil) @@ -175,7 +175,7 @@ func TestMergeSplitManySmallMetrics(t *testing.T) { func BenchmarkSplittingBasedOnItemCountManySmallMetrics(b *testing.B) { // All requests merge into a single batch. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 20020} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 20020} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newMetricsRequest(testdata.GenerateMetrics(10), nil)} @@ -190,7 +190,7 @@ func BenchmarkSplittingBasedOnItemCountManySmallMetrics(b *testing.B) { func BenchmarkSplittingBasedOnItemCountManyMetricsSlightlyAboveLimit(b *testing.B) { // Every incoming request results in a split. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 20000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 20000} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newMetricsRequest(testdata.GenerateMetrics(0), nil)} @@ -205,7 +205,7 @@ func BenchmarkSplittingBasedOnItemCountManyMetricsSlightlyAboveLimit(b *testing. func BenchmarkSplittingBasedOnItemCountHugeMetrics(b *testing.B) { // One request splits into many batches. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 20000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 20000} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newMetricsRequest(testdata.GenerateMetrics(0), nil)} diff --git a/exporter/exporterhelper/metrics_test.go b/exporter/exporterhelper/metrics_test.go index 82303a222cb..6d15988c443 100644 --- a/exporter/exporterhelper/metrics_test.go +++ b/exporter/exporterhelper/metrics_test.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper/internal" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/metadatatest" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/oteltest" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/requesttest" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/exporter/internal/storagetest" @@ -428,7 +429,7 @@ func checkWrapSpanForMetrics(t *testing.T, sr *tracetest.SpanRecorder, tracer tr require.Equalf(t, fakeMetricsParentSpanName, parentSpan.Name(), "SpanData %v", parentSpan) for _, sd := range gotSpanData[:numRequests] { require.Equalf(t, parentSpan.SpanContext(), sd.Parent(), "Exporter span not a child\nSpanData %v", sd) - internal.CheckStatus(t, sd, wantError) + oteltest.CheckStatus(t, sd, wantError) sentMetricPoints := int64(2) failedToSendMetricPoints := int64(0) diff --git a/exporter/exporterhelper/traces.go b/exporter/exporterhelper/traces.go index 03cc3f1566d..602917a7306 100644 --- a/exporter/exporterhelper/traces.go +++ b/exporter/exporterhelper/traces.go @@ -131,19 +131,23 @@ func NewTracesRequest( return nil, err } - tc, err := consumer.NewTraces(func(ctx context.Context, td ptrace.Traces) error { - req, cErr := converter(ctx, td) - if cErr != nil { - set.Logger.Error("Failed to convert traces. Dropping data.", + tc, err := consumer.NewTraces(newConsumeTraces(converter, be, set.Logger), be.ConsumerOptions...) + if err != nil { + return nil, err + } + + return &tracesExporter{BaseExporter: be, Traces: tc}, nil +} + +func newConsumeTraces(converter RequestFromTracesFunc, be *internal.BaseExporter, logger *zap.Logger) consumer.ConsumeTracesFunc { + return func(ctx context.Context, td ptrace.Traces) error { + req, err := converter(ctx, td) + if err != nil { + logger.Error("Failed to convert metrics. Dropping data.", zap.Int("dropped_spans", td.SpanCount()), zap.Error(err)) - return consumererror.NewPermanent(cErr) + return consumererror.NewPermanent(err) } return be.Send(ctx, req) - }, be.ConsumerOptions...) - - return &tracesExporter{ - BaseExporter: be, - Traces: tc, - }, err + } } diff --git a/exporter/exporterhelper/traces_batch.go b/exporter/exporterhelper/traces_batch.go index 91b936ec611..c7cf43c6b1d 100644 --- a/exporter/exporterhelper/traces_batch.go +++ b/exporter/exporterhelper/traces_batch.go @@ -13,7 +13,7 @@ import ( // MergeSplit splits and/or merges the provided traces request and the current request into one or more requests // conforming with the MaxSizeConfig. -func (req *tracesRequest) MergeSplit(_ context.Context, cfg exporterbatcher.MaxSizeConfig, r2 Request) ([]Request, error) { +func (req *tracesRequest) MergeSplit(_ context.Context, cfg exporterbatcher.SizeConfig, r2 Request) ([]Request, error) { if r2 != nil { req2, ok := r2.(*tracesRequest) if !ok { @@ -23,7 +23,7 @@ func (req *tracesRequest) MergeSplit(_ context.Context, cfg exporterbatcher.MaxS } // If no limit we can simply merge the new request into the current and return. - if cfg.MaxSizeItems == 0 { + if cfg.MaxSize == 0 { return []Request{req}, nil } return req.split(cfg) @@ -35,10 +35,10 @@ func (req *tracesRequest) mergeTo(dst *tracesRequest) { req.td.ResourceSpans().MoveAndAppendTo(dst.td.ResourceSpans()) } -func (req *tracesRequest) split(cfg exporterbatcher.MaxSizeConfig) ([]Request, error) { +func (req *tracesRequest) split(cfg exporterbatcher.SizeConfig) ([]Request, error) { var res []Request - for req.ItemsCount() > cfg.MaxSizeItems { - td := extractTraces(req.td, cfg.MaxSizeItems) + for req.ItemsCount() > cfg.MaxSize { + td := extractTraces(req.td, cfg.MaxSize) size := td.SpanCount() req.setCachedItemsCount(req.ItemsCount() - size) res = append(res, &tracesRequest{td: td, pusher: req.pusher, cachedItemsCount: size}) diff --git a/exporter/exporterhelper/traces_batch_test.go b/exporter/exporterhelper/traces_batch_test.go index 7444273260e..2dc8f3b4f5f 100644 --- a/exporter/exporterhelper/traces_batch_test.go +++ b/exporter/exporterhelper/traces_batch_test.go @@ -18,7 +18,7 @@ import ( func TestMergeTraces(t *testing.T) { tr1 := newTracesRequest(testdata.GenerateTraces(2), nil) tr2 := newTracesRequest(testdata.GenerateTraces(3), nil) - res, err := tr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, tr2) + res, err := tr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, tr2) require.NoError(t, err) assert.Equal(t, 5, res[0].ItemsCount()) } @@ -26,49 +26,49 @@ func TestMergeTraces(t *testing.T) { func TestMergeTracesInvalidInput(t *testing.T) { tr1 := newLogsRequest(testdata.GenerateLogs(2), nil) tr2 := newTracesRequest(testdata.GenerateTraces(3), nil) - _, err := tr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, tr2) + _, err := tr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, tr2) require.Error(t, err) } func TestMergeSplitTraces(t *testing.T) { tests := []struct { name string - cfg exporterbatcher.MaxSizeConfig + cfg exporterbatcher.SizeConfig tr1 Request tr2 Request expected []Request }{ { name: "both_requests_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(ptrace.NewTraces(), nil), tr2: newTracesRequest(ptrace.NewTraces(), nil), expected: []Request{newTracesRequest(ptrace.NewTraces(), nil)}, }, { name: "first_request_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(ptrace.NewTraces(), nil), tr2: newTracesRequest(testdata.GenerateTraces(5), nil), expected: []Request{newTracesRequest(testdata.GenerateTraces(5), nil)}, }, { name: "second_request_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(testdata.GenerateTraces(5), nil), tr2: newTracesRequest(ptrace.NewTraces(), nil), expected: []Request{newTracesRequest(testdata.GenerateTraces(5), nil)}, }, { name: "first_empty_second_nil", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(ptrace.NewTraces(), nil), tr2: nil, expected: []Request{newTracesRequest(ptrace.NewTraces(), nil)}, }, { name: "merge_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(testdata.GenerateTraces(5), nil), tr2: newTracesRequest(testdata.GenerateTraces(5), nil), expected: []Request{newTracesRequest(func() ptrace.Traces { @@ -79,7 +79,7 @@ func TestMergeSplitTraces(t *testing.T) { }, { name: "split_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 4}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 4}, tr1: newTracesRequest(ptrace.NewTraces(), nil), tr2: newTracesRequest(testdata.GenerateTraces(10), nil), expected: []Request{ @@ -90,7 +90,7 @@ func TestMergeSplitTraces(t *testing.T) { }, { name: "split_and_merge", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(testdata.GenerateTraces(4), nil), tr2: newTracesRequest(testdata.GenerateTraces(20), nil), expected: []Request{ @@ -105,7 +105,7 @@ func TestMergeSplitTraces(t *testing.T) { }, { name: "scope_spans_split", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, tr1: newTracesRequest(func() ptrace.Traces { td := testdata.GenerateTraces(10) extraScopeTraces := testdata.GenerateTraces(5) @@ -139,7 +139,7 @@ func TestMergeSplitTraces(t *testing.T) { func TestMergeSplitTracesInputNotModifiedIfErrorReturned(t *testing.T) { r1 := newTracesRequest(testdata.GenerateTraces(18), nil) r2 := newLogsRequest(testdata.GenerateLogs(3), nil) - _, err := r1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, r2) + _, err := r1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, r2) require.Error(t, err) assert.Equal(t, 18, r1.ItemsCount()) } @@ -147,7 +147,7 @@ func TestMergeSplitTracesInputNotModifiedIfErrorReturned(t *testing.T) { func TestMergeSplitTracesInvalidInput(t *testing.T) { r1 := newTracesRequest(testdata.GenerateTraces(2), nil) r2 := newMetricsRequest(testdata.GenerateMetrics(3), nil) - _, err := r1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, r2) + _, err := r1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, r2) require.Error(t, err) } @@ -161,7 +161,7 @@ func TestExtractTraces(t *testing.T) { } func TestMergeSplitManySmallTraces(t *testing.T) { - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} merged := []Request{newTracesRequest(testdata.GenerateTraces(1), nil)} for j := 0; j < 1000; j++ { lr2 := newTracesRequest(testdata.GenerateTraces(10), nil) @@ -173,7 +173,7 @@ func TestMergeSplitManySmallTraces(t *testing.T) { func BenchmarkSplittingBasedOnItemCountManySmallTraces(b *testing.B) { // All requests merge into a single batch. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10010} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10010} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newTracesRequest(testdata.GenerateTraces(10), nil)} @@ -188,7 +188,7 @@ func BenchmarkSplittingBasedOnItemCountManySmallTraces(b *testing.B) { func BenchmarkSplittingBasedOnItemCountManyTracesSlightlyAboveLimit(b *testing.B) { // Every incoming request results in a split. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newTracesRequest(testdata.GenerateTraces(0), nil)} @@ -203,7 +203,7 @@ func BenchmarkSplittingBasedOnItemCountManyTracesSlightlyAboveLimit(b *testing.B func BenchmarkSplittingBasedOnItemCountHugeTraces(b *testing.B) { // One request splits into many batches. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} b.ReportAllocs() for i := 0; i < b.N; i++ { merged := []Request{newTracesRequest(testdata.GenerateTraces(0), nil)} diff --git a/exporter/exporterhelper/traces_test.go b/exporter/exporterhelper/traces_test.go index 5a9d9e9d5d1..a032d963a1a 100644 --- a/exporter/exporterhelper/traces_test.go +++ b/exporter/exporterhelper/traces_test.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper/internal" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/metadatatest" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/oteltest" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/requesttest" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/exporter/internal/storagetest" @@ -430,7 +431,7 @@ func checkWrapSpanForTraces(t *testing.T, sr *tracetest.SpanRecorder, tracer tra for _, sd := range gotSpanData[:numRequests] { require.Equalf(t, parentSpan.SpanContext(), sd.Parent(), "Exporter span not a child\nSpanData %v", sd) - internal.CheckStatus(t, sd, wantError) + oteltest.CheckStatus(t, sd, wantError) sentSpans := int64(1) failedToSendSpans := int64(0) diff --git a/exporter/exporterhelper/xexporterhelper/go.mod b/exporter/exporterhelper/xexporterhelper/go.mod index 575d3b5ee1b..ad770798533 100644 --- a/exporter/exporterhelper/xexporterhelper/go.mod +++ b/exporter/exporterhelper/xexporterhelper/go.mod @@ -29,14 +29,21 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-version v1.7.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 + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/confmap v1.26.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.120.0 // indirect go.opentelemetry.io/collector/featuregate v1.26.0 // indirect diff --git a/exporter/exporterhelper/xexporterhelper/profiles.go b/exporter/exporterhelper/xexporterhelper/profiles.go index 0ac80e79f87..f1daf606f43 100644 --- a/exporter/exporterhelper/xexporterhelper/profiles.go +++ b/exporter/exporterhelper/xexporterhelper/profiles.go @@ -134,19 +134,23 @@ func NewProfilesRequestExporter( return nil, err } - tc, err := xconsumer.NewProfiles(func(ctx context.Context, pd pprofile.Profiles) error { - req, cErr := converter(ctx, pd) - if cErr != nil { - set.Logger.Error("Failed to convert profiles. Dropping data.", + tc, err := xconsumer.NewProfiles(newConsumeProfiles(converter, be, set.Logger), be.ConsumerOptions...) + if err != nil { + return nil, err + } + + return &profileExporter{BaseExporter: be, Profiles: tc}, nil +} + +func newConsumeProfiles(converter RequestFromProfilesFunc, be *internal.BaseExporter, logger *zap.Logger) xconsumer.ConsumeProfilesFunc { + return func(ctx context.Context, pd pprofile.Profiles) error { + req, err := converter(ctx, pd) + if err != nil { + logger.Error("Failed to convert metrics. Dropping data.", zap.Int("dropped_samples", pd.SampleCount()), zap.Error(err)) - return consumererror.NewPermanent(cErr) + return consumererror.NewPermanent(err) } return be.Send(ctx, req) - }, be.ConsumerOptions...) - - return &profileExporter{ - BaseExporter: be, - Profiles: tc, - }, err + } } diff --git a/exporter/exporterhelper/xexporterhelper/profiles_batch.go b/exporter/exporterhelper/xexporterhelper/profiles_batch.go index 6efd95d82c3..0dfa288cb23 100644 --- a/exporter/exporterhelper/xexporterhelper/profiles_batch.go +++ b/exporter/exporterhelper/xexporterhelper/profiles_batch.go @@ -13,7 +13,7 @@ import ( ) // MergeSplit splits and/or merges the profiles into multiple requests based on the MaxSizeConfig. -func (req *profilesRequest) MergeSplit(_ context.Context, cfg exporterbatcher.MaxSizeConfig, r2 exporterhelper.Request) ([]exporterhelper.Request, error) { +func (req *profilesRequest) MergeSplit(_ context.Context, cfg exporterbatcher.SizeConfig, r2 exporterhelper.Request) ([]exporterhelper.Request, error) { if r2 != nil { req2, ok := r2.(*profilesRequest) if !ok { @@ -23,7 +23,7 @@ func (req *profilesRequest) MergeSplit(_ context.Context, cfg exporterbatcher.Ma } // If no limit we can simply merge the new request into the current and return. - if cfg.MaxSizeItems == 0 { + if cfg.MaxSize == 0 { return []exporterhelper.Request{req}, nil } return req.split(cfg) @@ -35,10 +35,10 @@ func (req *profilesRequest) mergeTo(dst *profilesRequest) { req.pd.ResourceProfiles().MoveAndAppendTo(dst.pd.ResourceProfiles()) } -func (req *profilesRequest) split(cfg exporterbatcher.MaxSizeConfig) ([]exporterhelper.Request, error) { +func (req *profilesRequest) split(cfg exporterbatcher.SizeConfig) ([]exporterhelper.Request, error) { var res []exporterhelper.Request - for req.ItemsCount() > cfg.MaxSizeItems { - pd := extractProfiles(req.pd, cfg.MaxSizeItems) + for req.ItemsCount() > cfg.MaxSize { + pd := extractProfiles(req.pd, cfg.MaxSize) size := pd.SampleCount() req.setCachedItemsCount(req.ItemsCount() - size) res = append(res, &profilesRequest{pd: pd, pusher: req.pusher, cachedItemsCount: size}) diff --git a/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go b/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go index ff2a8e725e6..ff1435c1627 100644 --- a/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go +++ b/exporter/exporterhelper/xexporterhelper/profiles_batch_test.go @@ -20,7 +20,7 @@ import ( func TestMergeProfiles(t *testing.T) { pr1 := newProfilesRequest(testdata.GenerateProfiles(2), nil) pr2 := newProfilesRequest(testdata.GenerateProfiles(3), nil) - res, err := pr1.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, pr2) + res, err := pr1.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, pr2) require.NoError(t, err) assert.Len(t, res, 1) assert.Equal(t, 5, res[0].ItemsCount()) @@ -28,42 +28,42 @@ func TestMergeProfiles(t *testing.T) { func TestMergeProfilesInvalidInput(t *testing.T) { pr2 := newProfilesRequest(testdata.GenerateProfiles(3), nil) - _, err := pr2.MergeSplit(context.Background(), exporterbatcher.MaxSizeConfig{}, &requesttest.FakeRequest{Items: 1}) + _, err := pr2.MergeSplit(context.Background(), exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems}, &requesttest.FakeRequest{Items: 1}) assert.Error(t, err) } func TestMergeSplitProfiles(t *testing.T) { tests := []struct { name string - cfg exporterbatcher.MaxSizeConfig + cfg exporterbatcher.SizeConfig pr1 exporterhelper.Request pr2 exporterhelper.Request expected []exporterhelper.Request }{ { name: "both_requests_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, pr1: newProfilesRequest(pprofile.NewProfiles(), nil), pr2: newProfilesRequest(pprofile.NewProfiles(), nil), expected: []exporterhelper.Request{newProfilesRequest(pprofile.NewProfiles(), nil)}, }, { name: "first_request_empty", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, pr1: newProfilesRequest(pprofile.NewProfiles(), nil), pr2: newProfilesRequest(testdata.GenerateProfiles(5), nil), expected: []exporterhelper.Request{newProfilesRequest(testdata.GenerateProfiles(5), nil)}, }, { name: "first_empty_second_nil", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, pr1: newProfilesRequest(pprofile.NewProfiles(), nil), pr2: nil, expected: []exporterhelper.Request{newProfilesRequest(pprofile.NewProfiles(), nil)}, }, { name: "merge_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, pr1: newProfilesRequest(testdata.GenerateProfiles(4), nil), pr2: newProfilesRequest(testdata.GenerateProfiles(6), nil), expected: []exporterhelper.Request{newProfilesRequest(func() pprofile.Profiles { @@ -74,7 +74,7 @@ func TestMergeSplitProfiles(t *testing.T) { }, { name: "split_only", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 4}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 4}, pr1: newProfilesRequest(testdata.GenerateProfiles(10), nil), pr2: nil, expected: []exporterhelper.Request{ @@ -85,7 +85,7 @@ func TestMergeSplitProfiles(t *testing.T) { }, { name: "merge_and_split", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 10}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10}, pr1: newProfilesRequest(testdata.GenerateProfiles(8), nil), pr2: newProfilesRequest(testdata.GenerateProfiles(20), nil), expected: []exporterhelper.Request{ @@ -100,7 +100,7 @@ func TestMergeSplitProfiles(t *testing.T) { }, { name: "scope_profiles_split", - cfg: exporterbatcher.MaxSizeConfig{MaxSizeItems: 4}, + cfg: exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 4}, pr1: newProfilesRequest(func() pprofile.Profiles { return testdata.GenerateProfiles(6) }(), nil), @@ -136,7 +136,7 @@ func TestExtractProfiles(t *testing.T) { func TestMergeSplitManySmallLogs(t *testing.T) { // All requests merge into a single batch. - cfg := exporterbatcher.MaxSizeConfig{MaxSizeItems: 10000} + cfg := exporterbatcher.SizeConfig{Sizer: exporterbatcher.SizerTypeItems, MaxSize: 10000} merged := []exporterhelper.Request{newProfilesRequest(testdata.GenerateProfiles(1), nil)} for j := 0; j < 1000; j++ { lr2 := newProfilesRequest(testdata.GenerateProfiles(10), nil) diff --git a/exporter/exporterhelper/xexporterhelper/profiles_test.go b/exporter/exporterhelper/xexporterhelper/profiles_test.go index 0fb0561a546..a064f86de46 100644 --- a/exporter/exporterhelper/xexporterhelper/profiles_test.go +++ b/exporter/exporterhelper/xexporterhelper/profiles_test.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper" "go.opentelemetry.io/collector/exporter/exporterhelper/internal" + "go.opentelemetry.io/collector/exporter/exporterhelper/internal/oteltest" "go.opentelemetry.io/collector/exporter/exporterhelper/internal/requesttest" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/exporter/internal/storagetest" @@ -312,7 +313,7 @@ func checkWrapSpanForProfilesExporter(t *testing.T, sr *tracetest.SpanRecorder, require.Equalf(t, fakeProfilesParentSpanName, parentSpan.Name(), "SpanData %v", parentSpan) for _, sd := range gotSpanData[:numRequests] { require.Equalf(t, parentSpan.SpanContext(), sd.Parent(), "Exporter span not a child\nSpanData %v", sd) - internal.CheckStatus(t, sd, wantError) + oteltest.CheckStatus(t, sd, wantError) sentSampleRecords := int64(1) failedToSendSampleRecords := int64(0) diff --git a/exporter/exportertest/contract_checker.go b/exporter/exportertest/contract_checker.go index 7a3a61ff5f4..bc0eb9324b2 100644 --- a/exporter/exportertest/contract_checker.go +++ b/exporter/exportertest/contract_checker.go @@ -151,7 +151,7 @@ func checkTraces(t *testing.T, params CheckConsumeContractParams, mockReceiver c ctx := context.Background() var exp exporter.Traces var err error - exp, err = params.ExporterFactory.CreateTraces(ctx, NewNopSettings(NopType), params.ExporterConfig) + exp, err = params.ExporterFactory.CreateTraces(ctx, NewNopSettings(params.ExporterFactory.Type()), params.ExporterConfig) require.NoError(t, err) require.NotNil(t, exp) @@ -191,7 +191,7 @@ func checkLogs(t *testing.T, params CheckConsumeContractParams, mockReceiver com ctx := context.Background() var exp exporter.Logs var err error - exp, err = params.ExporterFactory.CreateLogs(ctx, NewNopSettings(NopType), params.ExporterConfig) + exp, err = params.ExporterFactory.CreateLogs(ctx, NewNopSettings(params.ExporterFactory.Type()), params.ExporterConfig) require.NoError(t, err) require.NotNil(t, exp) diff --git a/exporter/exportertest/go.mod b/exporter/exportertest/go.mod index 60187e1abce..af6b41be8be 100644 --- a/exporter/exportertest/go.mod +++ b/exporter/exportertest/go.mod @@ -26,13 +26,20 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/hashicorp/go-version v1.7.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 + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/confmap v1.26.0 // indirect go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.120.0 // indirect diff --git a/exporter/internal/experr/err.go b/exporter/internal/experr/err.go index 6bff64b162d..f8b109d7de7 100644 --- a/exporter/internal/experr/err.go +++ b/exporter/internal/experr/err.go @@ -3,7 +3,12 @@ package experr // import "go.opentelemetry.io/collector/exporter/internal/experr" -import "errors" +import ( + "errors" + "fmt" + + "go.opentelemetry.io/collector/component" +) type shutdownErr struct { err error @@ -25,3 +30,7 @@ func IsShutdownErr(err error) bool { var sdErr shutdownErr return errors.As(err, &sdErr) } + +func ErrIDMismatch(id component.ID, typ component.Type) error { + return fmt.Errorf("component type mismatch: component ID %q does not have type %q", id, typ) +} diff --git a/exporter/otlpexporter/config_test.go b/exporter/otlpexporter/config_test.go index 099d67958a4..50706d655af 100644 --- a/exporter/otlpexporter/config_test.go +++ b/exporter/otlpexporter/config_test.go @@ -37,6 +37,7 @@ func TestUnmarshalConfig(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() require.NoError(t, cm.Unmarshal(&cfg)) + require.NoError(t, xconfmap.Validate(&cfg)) assert.Equal(t, &Config{ TimeoutConfig: exporterhelper.TimeoutConfig{ @@ -58,11 +59,10 @@ func TestUnmarshalConfig(t *testing.T) { BatcherConfig: exporterbatcher.Config{ Enabled: true, FlushTimeout: 200 * time.Millisecond, - MinSizeConfig: exporterbatcher.MinSizeConfig{ - MinSizeItems: 1000, - }, - MaxSizeConfig: exporterbatcher.MaxSizeConfig{ - MaxSizeItems: 10000, + SizeConfig: exporterbatcher.SizeConfig{ + Sizer: exporterbatcher.SizerTypeItems, + MinSize: 1000, + MaxSize: 10000, }, }, ClientConfig: configgrpc.ClientConfig{ diff --git a/exporter/otlpexporter/go.mod b/exporter/otlpexporter/go.mod index f514ea4edbb..8edb5bf1496 100644 --- a/exporter/otlpexporter/go.mod +++ b/exporter/otlpexporter/go.mod @@ -60,7 +60,7 @@ require ( go.opentelemetry.io/collector/consumer/consumertest v0.120.0 // indirect go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/extension/xextension v0.120.0 // indirect go.opentelemetry.io/collector/featuregate v1.26.0 // indirect go.opentelemetry.io/collector/pipeline v0.120.0 // indirect @@ -105,7 +105,7 @@ replace go.opentelemetry.io/collector/exporter => ../ replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/pdata => ../../pdata @@ -152,7 +152,7 @@ replace go.opentelemetry.io/collector/exporter/exportertest => ../exportertest replace go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/featuregate => ../../featuregate diff --git a/exporter/otlpexporter/testdata/config.yaml b/exporter/otlpexporter/testdata/config.yaml index d26631053e1..ed9a520ea67 100644 --- a/exporter/otlpexporter/testdata/config.yaml +++ b/exporter/otlpexporter/testdata/config.yaml @@ -17,8 +17,9 @@ retry_on_failure: batcher: enabled: true flush_timeout: 200ms - min_size_items: 1000 - max_size_items: 10000 + sizer: "items" + min_size: 1000 + max_size: 10000 auth: authenticator: nop headers: diff --git a/exporter/otlphttpexporter/go.mod b/exporter/otlphttpexporter/go.mod index 0f8a2bd3f1b..2b00255f491 100644 --- a/exporter/otlphttpexporter/go.mod +++ b/exporter/otlphttpexporter/go.mod @@ -60,7 +60,7 @@ require ( go.opentelemetry.io/collector/consumer/consumertest v0.120.0 // indirect go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/extension/xextension v0.120.0 // indirect go.opentelemetry.io/collector/featuregate v1.26.0 // indirect go.opentelemetry.io/collector/pipeline v0.120.0 // indirect @@ -105,7 +105,7 @@ replace go.opentelemetry.io/collector/exporter => ../ replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/pdata => ../../pdata @@ -150,7 +150,7 @@ replace go.opentelemetry.io/collector/exporter/exportertest => ../exportertest replace go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/featuregate => ../../featuregate diff --git a/exporter/xexporter/exporter.go b/exporter/xexporter/exporter.go index 8add9a9aba8..a3010008041 100644 --- a/exporter/xexporter/exporter.go +++ b/exporter/xexporter/exporter.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/xconsumer" "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/internal/experr" "go.opentelemetry.io/collector/pipeline" ) @@ -95,6 +96,9 @@ func (f *factory) CreateProfiles(ctx context.Context, set exporter.Settings, cfg return nil, pipeline.ErrSignalNotSupported } + if set.ID.Type() != f.Type() { + return nil, experr.ErrIDMismatch(set.ID, f.Type()) + } return f.createProfilesFunc(ctx, set, cfg) } diff --git a/exporter/xexporter/exporter_test.go b/exporter/xexporter/exporter_test.go index d1929c4c22e..1ac8d0886db 100644 --- a/exporter/xexporter/exporter_test.go +++ b/exporter/xexporter/exporter_test.go @@ -8,12 +8,16 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/internal/experr" ) +var testID = component.MustNewID("test") + func TestNewFactoryWithProfiles(t *testing.T) { testType := component.MustNewType("test") defaultCfg := struct{}{} @@ -26,8 +30,13 @@ func TestNewFactoryWithProfiles(t *testing.T) { assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) assert.Equal(t, component.StabilityLevelDevelopment, factory.ProfilesStability()) - _, err := factory.CreateProfiles(context.Background(), exporter.Settings{}, &defaultCfg) - assert.NoError(t, err) + _, err := factory.CreateProfiles(context.Background(), exporter.Settings{ID: testID}, &defaultCfg) + require.NoError(t, err) + + wrongID := component.MustNewID("wrong") + wrongIDErrStr := experr.ErrIDMismatch(wrongID, testType).Error() + _, err = factory.CreateProfiles(context.Background(), exporter.Settings{ID: wrongID}, &defaultCfg) + assert.EqualError(t, err, wrongIDErrStr) } var nopInstance = &nop{ diff --git a/extension/auth/authtest/go.mod b/extension/auth/authtest/go.mod index 04fea1e650e..622539407e1 100644 --- a/extension/auth/authtest/go.mod +++ b/extension/auth/authtest/go.mod @@ -1,20 +1,15 @@ +// Deprecated: use go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest instead. module go.opentelemetry.io/collector/extension/auth/authtest go 1.23.0 -require ( - github.com/stretchr/testify v1.10.0 - go.opentelemetry.io/collector/component v0.120.0 - go.opentelemetry.io/collector/extension/auth v0.120.0 - go.uber.org/goleak v1.3.0 - google.golang.org/grpc v1.70.0 -) +require go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest v0.0.0-20250227134758-6a98ee8b14a2 require ( - github.com/davecgh/go-spew v1.1.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/component v0.120.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/pdata v1.26.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect @@ -25,11 +20,13 @@ require ( golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace go.opentelemetry.io/collector/extension/auth => .. +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extensionauth + +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extensionauth/extensionauthtest replace go.opentelemetry.io/collector/component => ../../../component diff --git a/extension/auth/authtest/go.sum b/extension/auth/authtest/go.sum index 55710e1d6d5..fccece3ec88 100644 --- a/extension/auth/authtest/go.sum +++ b/extension/auth/authtest/go.sum @@ -14,14 +14,8 @@ 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/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/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/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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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= @@ -81,8 +75,5 @@ google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/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/extension/auth/authtest/mock_clientauth.go b/extension/auth/authtest/mock_clientauth.go index d28546fc4a0..699971e9324 100644 --- a/extension/auth/authtest/mock_clientauth.go +++ b/extension/auth/authtest/mock_clientauth.go @@ -4,52 +4,9 @@ package authtest // import "go.opentelemetry.io/collector/extension/auth/authtest" import ( - "context" - "errors" - "net/http" - - "google.golang.org/grpc/credentials" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/extension/auth" -) - -var ( - _ auth.Client = (*MockClient)(nil) - errMockError = errors.New("mock Error") + "go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest" ) // MockClient provides a mock implementation of GRPCClient and HTTPClient interfaces -type MockClient struct { - ResultRoundTripper http.RoundTripper - ResultPerRPCCredentials credentials.PerRPCCredentials - MustError bool -} - -// Start for the MockClient does nothing -func (m *MockClient) Start(context.Context, component.Host) error { - return nil -} - -// Shutdown for the MockClient does nothing -func (m *MockClient) Shutdown(context.Context) error { - return nil -} - -// RoundTripper for the MockClient either returns error if the mock authenticator is forced to or -// returns the supplied resultRoundTripper. -func (m *MockClient) RoundTripper(http.RoundTripper) (http.RoundTripper, error) { - if m.MustError { - return nil, errMockError - } - return m.ResultRoundTripper, nil -} - -// PerRPCCredentials for the MockClient either returns error if the mock authenticator is forced to or -// returns the supplied resultPerRPCCredentials. -func (m *MockClient) PerRPCCredentials() (credentials.PerRPCCredentials, error) { - if m.MustError { - return nil, errMockError - } - return m.ResultPerRPCCredentials, nil -} +// Deprecated: [v0.121.0] Use extensionauthtest.MockClient instead. +type MockClient = extensionauthtest.MockClient diff --git a/extension/auth/client.go b/extension/auth/client.go index 03eba4ed336..7e031eeafab 100644 --- a/extension/auth/client.go +++ b/extension/auth/client.go @@ -4,104 +4,62 @@ package auth // import "go.opentelemetry.io/collector/extension/auth" import ( - "net/http" - - "google.golang.org/grpc/credentials" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/extension" + "go.opentelemetry.io/collector/extension/extensionauth" ) // Client is an Extension that can be used as an authenticator for the configauth.Authentication option. // Authenticators are then included as part of OpenTelemetry Collector builds and can be referenced by their // names from the Authentication configuration. -type Client interface { - extension.Extension - - // RoundTripper returns a RoundTripper that can be used to authenticate HTTP requests. - RoundTripper(base http.RoundTripper) (http.RoundTripper, error) - - // PerRPCCredentials returns a PerRPCCredentials that can be used to authenticate gRPC requests. - PerRPCCredentials() (credentials.PerRPCCredentials, error) -} +// Deprecated: [v0.121.0] Use extensionauth.Client instead. +type Client = extensionauth.Client // ClientOption represents the possible options for NewClient. -type ClientOption interface { - apply(*defaultClient) -} - -type clientOptionFunc func(*defaultClient) - -func (of clientOptionFunc) apply(e *defaultClient) { - of(e) -} +// Deprecated: [v0.121.0] Use extensionauth.Client instead. +type ClientOption = extensionauth.ClientOption // ClientRoundTripperFunc specifies the function that returns a RoundTripper that can be used to authenticate HTTP requests. -type ClientRoundTripperFunc func(base http.RoundTripper) (http.RoundTripper, error) - -func (f ClientRoundTripperFunc) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { - if f == nil { - return base, nil - } - return f(base) -} +// Deprecated: [v0.121.0] Use extensionauth.ClientRoundTripperFunc instead. +type ClientRoundTripperFunc = extensionauth.ClientRoundTripperFunc // ClientPerRPCCredentialsFunc specifies the function that returns a PerRPCCredentials that can be used to authenticate gRPC requests. -type ClientPerRPCCredentialsFunc func() (credentials.PerRPCCredentials, error) - -func (f ClientPerRPCCredentialsFunc) PerRPCCredentials() (credentials.PerRPCCredentials, error) { - if f == nil { - return nil, nil - } - return f() -} - -type defaultClient struct { - component.StartFunc - component.ShutdownFunc - ClientRoundTripperFunc - ClientPerRPCCredentialsFunc -} +// Deprecated: [v0.121.0] Use extensionauth.ClientPerRPCCredentialsFunc instead. +type ClientPerRPCCredentialsFunc = extensionauth.ClientPerRPCCredentialsFunc // WithClientStart overrides the default `Start` function for a component.Component. // The default always returns nil. +// Deprecated: [v0.121.0] Use extensionauth.WithClientStart instead. func WithClientStart(startFunc component.StartFunc) ClientOption { - return clientOptionFunc(func(o *defaultClient) { - o.StartFunc = startFunc - }) + return extensionauth.WithClientStart(startFunc) } // WithClientShutdown overrides the default `Shutdown` function for a component.Component. // The default always returns nil. +// Deprecated: [v0.121.0] Use extensionauth.WithClientShutdown instead. func WithClientShutdown(shutdownFunc component.ShutdownFunc) ClientOption { - return clientOptionFunc(func(o *defaultClient) { - o.ShutdownFunc = shutdownFunc - }) + return extensionauth.WithClientShutdown(shutdownFunc) } // WithClientRoundTripper provides a `RoundTripper` function for this client authenticator. // The default round tripper is no-op. +// Deprecated: [v0.121.0] Use extensionauth.WithClientRoundTripper instead. func WithClientRoundTripper(roundTripperFunc ClientRoundTripperFunc) ClientOption { - return clientOptionFunc(func(o *defaultClient) { - o.ClientRoundTripperFunc = roundTripperFunc - }) + return extensionauth.WithClientRoundTripper(roundTripperFunc) } // WithClientPerRPCCredentials provides a `PerRPCCredentials` function for this client authenticator. // There's no default. +// Deprecated: [v0.121.0] Use extensionauth.WithClientPerRPCCredentials instead. func WithClientPerRPCCredentials(perRPCCredentialsFunc ClientPerRPCCredentialsFunc) ClientOption { - return clientOptionFunc(func(o *defaultClient) { - o.ClientPerRPCCredentialsFunc = perRPCCredentialsFunc - }) + return extensionauth.WithClientPerRPCCredentials(perRPCCredentialsFunc) } // NewClient returns a Client configured with the provided options. +// Deprecated: [v0.121.0] Use extensionauth.NewClient instead. func NewClient(options ...ClientOption) Client { - bc := &defaultClient{} - - for _, op := range options { - op.apply(bc) + client, err := extensionauth.NewClient(options...) + if err != nil { + panic("unexpected error when calling extensionauth.NewClient: " + err.Error()) } - - return bc + return client } diff --git a/extension/auth/go.mod b/extension/auth/go.mod index 4e7253b2e03..4e7695068ef 100644 --- a/extension/auth/go.mod +++ b/extension/auth/go.mod @@ -1,29 +1,19 @@ +// Deprecated: use go.opentelemetry.io/collector/extension/extensionauth instead. module go.opentelemetry.io/collector/extension/auth go 1.23.0 require ( - github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/component v0.120.0 - go.opentelemetry.io/collector/component/componenttest v0.120.0 - go.opentelemetry.io/collector/extension v0.120.0 - go.uber.org/goleak v1.3.0 - google.golang.org/grpc v1.70.0 + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/extension v0.120.0 // indirect go.opentelemetry.io/collector/pdata v1.26.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/sdk v1.34.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect @@ -31,8 +21,8 @@ require ( golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) replace go.opentelemetry.io/collector/component => ../../component @@ -41,4 +31,6 @@ replace go.opentelemetry.io/collector/component/componenttest => ../../component replace go.opentelemetry.io/collector/extension => ../ +replace go.opentelemetry.io/collector/extension/extensionauth => ../extensionauth + replace go.opentelemetry.io/collector/pdata => ../../pdata diff --git a/extension/auth/go.sum b/extension/auth/go.sum index 97b9d860e8e..fccece3ec88 100644 --- a/extension/auth/go.sum +++ b/extension/auth/go.sum @@ -1,6 +1,5 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -15,14 +14,8 @@ 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/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/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/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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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= @@ -82,8 +75,5 @@ google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/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/extension/auth/server.go b/extension/auth/server.go index 256dc51ce9c..b0a5c8ae5d5 100644 --- a/extension/auth/server.go +++ b/extension/auth/server.go @@ -4,10 +4,8 @@ package auth // import "go.opentelemetry.io/collector/extension/auth" import ( - "context" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/extension" + "go.opentelemetry.io/collector/extension/extensionauth" ) // Server is an Extension that can be used as an authenticator for the configauth.Authentication option. @@ -15,78 +13,44 @@ import ( // names from the Authentication configuration. Each Server is free to define its own behavior and configuration options, // but note that the expectations that come as part of Extensions exist here as well. For instance, multiple instances of the same // authenticator should be possible to exist under different names. -type Server interface { - extension.Extension - - // Authenticate checks whether the given map contains valid auth data. Successfully authenticated calls will always return a nil error. - // When the authentication fails, an error must be returned and the caller must not retry. This function is typically called from interceptors, - // on behalf of receivers, but receivers can still call this directly if the usage of interceptors isn't suitable. - // The deadline and cancellation given to this function must be respected, but note that authentication data has to be part of the map, not context. - // The resulting context should contain the authentication data, such as the principal/username, group membership (if available), and the raw - // authentication data (if possible). This will allow other components in the pipeline to make decisions based on that data, such as routing based - // on tenancy as determined by the group membership, or passing through the authentication data to the next collector/backend. - // The context keys to be used are not defined yet. - Authenticate(ctx context.Context, sources map[string][]string) (context.Context, error) -} - -type defaultServer struct { - ServerAuthenticateFunc - component.StartFunc - component.ShutdownFunc -} +// Deprecated: [v0.121.0] Use extensionauth.Server instead. +type Server = extensionauth.Server // ServerOption represents the possible options for NewServer. -type ServerOption interface { - apply(*defaultServer) -} - -type serverOptionFunc func(*defaultServer) - -func (of serverOptionFunc) apply(e *defaultServer) { - of(e) -} +// Deprecated: [v0.121.0] Use extensionauth.ServerOption instead. +type ServerOption = extensionauth.ServerOption // ServerAuthenticateFunc defines the signature for the function responsible for performing the authentication based // on the given sources map. See Server.Authenticate. -type ServerAuthenticateFunc func(ctx context.Context, sources map[string][]string) (context.Context, error) - -func (f ServerAuthenticateFunc) Authenticate(ctx context.Context, sources map[string][]string) (context.Context, error) { - if f == nil { - return ctx, nil - } - return f(ctx, sources) -} +// Deprecated: [v0.121.0] Use extensionauth.ServerAuthenticateFunc instead. +type ServerAuthenticateFunc = extensionauth.ServerAuthenticateFunc // WithServerAuthenticate specifies which function to use to perform the authentication. +// Deprecated: [v0.121.0] Use extensionauth.WithServerAuthenticate instead. func WithServerAuthenticate(authFunc ServerAuthenticateFunc) ServerOption { - return serverOptionFunc(func(o *defaultServer) { - o.ServerAuthenticateFunc = authFunc - }) + return extensionauth.WithServerAuthenticate(authFunc) } // WithServerStart overrides the default `Start` function for a component.Component. // The default always returns nil. +// Deprecated: [v0.121.0] Use extensionauth.WithServerStart instead. func WithServerStart(startFunc component.StartFunc) ServerOption { - return serverOptionFunc(func(o *defaultServer) { - o.StartFunc = startFunc - }) + return extensionauth.WithServerStart(startFunc) } // WithServerShutdown overrides the default `Shutdown` function for a component.Component. // The default always returns nil. +// Deprecated: [v0.121.0] Use extensionauth.WithServerShutdown instead. func WithServerShutdown(shutdownFunc component.ShutdownFunc) ServerOption { - return serverOptionFunc(func(o *defaultServer) { - o.ShutdownFunc = shutdownFunc - }) + return extensionauth.WithServerShutdown(shutdownFunc) } // NewServer returns a Server configured with the provided options. +// Deprecated: [v0.121.0] Use extensionauth.NewServer instead. func NewServer(options ...ServerOption) Server { - bc := &defaultServer{} - - for _, op := range options { - op.apply(bc) + srv, err := extensionauth.NewServer(options...) + if err != nil { + panic("unexpected error when calling extensionauth.NewServer: " + err.Error()) } - - return bc + return srv } diff --git a/extension/extensionauth/Makefile b/extension/extensionauth/Makefile new file mode 100644 index 00000000000..ded7a36092d --- /dev/null +++ b/extension/extensionauth/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/extension/extensionauth/client.go b/extension/extensionauth/client.go new file mode 100644 index 00000000000..f066651d855 --- /dev/null +++ b/extension/extensionauth/client.go @@ -0,0 +1,127 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package extensionauth // import "go.opentelemetry.io/collector/extension/extensionauth" + +import ( + "net/http" + + "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension" +) + +// Client is an Extension that can be used as an authenticator for the configauth.Authentication option. +// Authenticators are then included as part of OpenTelemetry Collector builds and can be referenced by their +// names from the Authentication configuration. +type Client interface { + extension.Extension + + // RoundTripper returns a RoundTripper that can be used to authenticate HTTP requests. + RoundTripper(base http.RoundTripper) (http.RoundTripper, error) + + // PerRPCCredentials returns a PerRPCCredentials that can be used to authenticate gRPC requests. + PerRPCCredentials() (credentials.PerRPCCredentials, error) +} + +// ClientOption represents the possible options for NewClient. +type ClientOption interface { + apply(*defaultClient) +} + +type clientOptionFunc func(*defaultClient) + +func (of clientOptionFunc) apply(e *defaultClient) { + of(e) +} + +// ClientRoundTripperFunc specifies the function that returns a RoundTripper that can be used to authenticate HTTP requests. +type ClientRoundTripperFunc func(base http.RoundTripper) (http.RoundTripper, error) + +// Deprecated: [v0.121.0] No longer used, will be removed. +func (f ClientRoundTripperFunc) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { + if f == nil { + return base, nil + } + return f(base) +} + +// ClientPerRPCCredentialsFunc specifies the function that returns a PerRPCCredentials that can be used to authenticate gRPC requests. +type ClientPerRPCCredentialsFunc func() (credentials.PerRPCCredentials, error) + +// Deprecated: [v0.121.0] No longer used, will be removed. +func (f ClientPerRPCCredentialsFunc) PerRPCCredentials() (credentials.PerRPCCredentials, error) { + if f == nil { + return nil, nil + } + return f() +} + +var _ Client = (*defaultClient)(nil) + +type defaultClient struct { + component.StartFunc + component.ShutdownFunc + clientRoundTripperFunc ClientRoundTripperFunc + clientPerRPCCredentialsFunc ClientPerRPCCredentialsFunc +} + +// PerRPCCredentials implements Client. +func (d *defaultClient) PerRPCCredentials() (credentials.PerRPCCredentials, error) { + if d.clientPerRPCCredentialsFunc == nil { + return nil, nil + } + return d.clientPerRPCCredentialsFunc() +} + +// RoundTripper implements Client. +func (d *defaultClient) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { + if d.clientRoundTripperFunc == nil { + return base, nil + } + return d.clientRoundTripperFunc(base) +} + +// WithClientStart overrides the default `Start` function for a component.Component. +// The default always returns nil. +func WithClientStart(startFunc component.StartFunc) ClientOption { + return clientOptionFunc(func(o *defaultClient) { + o.StartFunc = startFunc + }) +} + +// WithClientShutdown overrides the default `Shutdown` function for a component.Component. +// The default always returns nil. +func WithClientShutdown(shutdownFunc component.ShutdownFunc) ClientOption { + return clientOptionFunc(func(o *defaultClient) { + o.ShutdownFunc = shutdownFunc + }) +} + +// WithClientRoundTripper provides a `RoundTripper` function for this client authenticator. +// The default round tripper is no-op. +func WithClientRoundTripper(roundTripperFunc ClientRoundTripperFunc) ClientOption { + return clientOptionFunc(func(o *defaultClient) { + o.clientRoundTripperFunc = roundTripperFunc + }) +} + +// WithClientPerRPCCredentials provides a `PerRPCCredentials` function for this client authenticator. +// There's no default. +func WithClientPerRPCCredentials(perRPCCredentialsFunc ClientPerRPCCredentialsFunc) ClientOption { + return clientOptionFunc(func(o *defaultClient) { + o.clientPerRPCCredentialsFunc = perRPCCredentialsFunc + }) +} + +// NewClient returns a Client configured with the provided options. +func NewClient(options ...ClientOption) (Client, error) { + bc := &defaultClient{} + + for _, op := range options { + op.apply(bc) + } + + return bc, nil +} diff --git a/extension/auth/client_test.go b/extension/extensionauth/client_test.go similarity index 75% rename from extension/auth/client_test.go rename to extension/extensionauth/client_test.go index cb95a25daaf..c3e0ad1611e 100644 --- a/extension/auth/client_test.go +++ b/extension/extensionauth/client_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package auth +package extensionauth import ( "context" @@ -9,6 +9,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/grpc/credentials" "go.opentelemetry.io/collector/component" @@ -17,7 +18,8 @@ import ( func TestClientDefaultValues(t *testing.T) { // prepare - e := NewClient() + e, err := NewClient() + require.NoError(t, err) // test t.Run("start", func(t *testing.T) { @@ -45,13 +47,14 @@ func TestClientDefaultValues(t *testing.T) { func TestWithClientStart(t *testing.T) { called := false - e := NewClient(WithClientStart(func(context.Context, component.Host) error { + e, err := NewClient(WithClientStart(func(context.Context, component.Host) error { called = true return nil })) + require.NoError(t, err) // test - err := e.Start(context.Background(), componenttest.NewNopHost()) + err = e.Start(context.Background(), componenttest.NewNopHost()) // verify assert.True(t, called) @@ -60,13 +63,14 @@ func TestWithClientStart(t *testing.T) { func TestWithClientShutdown(t *testing.T) { called := false - e := NewClient(WithClientShutdown(func(context.Context) error { + e, err := NewClient(WithClientShutdown(func(context.Context) error { called = true return nil })) + require.NoError(t, err) // test - err := e.Shutdown(context.Background()) + err = e.Shutdown(context.Background()) // verify assert.True(t, called) @@ -75,10 +79,11 @@ func TestWithClientShutdown(t *testing.T) { func TestWithClientRoundTripper(t *testing.T) { called := false - e := NewClient(WithClientRoundTripper(func(base http.RoundTripper) (http.RoundTripper, error) { + e, err := NewClient(WithClientRoundTripper(func(base http.RoundTripper) (http.RoundTripper, error) { called = true return base, nil })) + require.NoError(t, err) // test rt, err := e.RoundTripper(http.DefaultTransport) @@ -103,10 +108,11 @@ func (c *customPerRPCCredentials) RequireTransportSecurity() bool { func TestWithPerRPCCredentials(t *testing.T) { called := false - e := NewClient(WithClientPerRPCCredentials(func() (credentials.PerRPCCredentials, error) { + e, err := NewClient(WithClientPerRPCCredentials(func() (credentials.PerRPCCredentials, error) { called = true return &customPerRPCCredentials{}, nil })) + require.NoError(t, err) // test p, err := e.PerRPCCredentials() diff --git a/extension/auth/doc.go b/extension/extensionauth/doc.go similarity index 74% rename from extension/auth/doc.go rename to extension/extensionauth/doc.go index f6e452df41a..948a823e548 100644 --- a/extension/auth/doc.go +++ b/extension/extensionauth/doc.go @@ -4,4 +4,4 @@ // Package auth implements the configuration settings to // ensure authentication on incoming requests, and allows // exporters to add authentication on outgoing requests. -package auth // import "go.opentelemetry.io/collector/extension/auth" +package extensionauth // import "go.opentelemetry.io/collector/extension/extensionauth" diff --git a/extension/extensionauth/extensionauthtest/Makefile b/extension/extensionauth/extensionauthtest/Makefile new file mode 100644 index 00000000000..bdd863a203b --- /dev/null +++ b/extension/extensionauth/extensionauthtest/Makefile @@ -0,0 +1 @@ +include ../../../Makefile.Common diff --git a/extension/extensionauth/extensionauthtest/go.mod b/extension/extensionauth/extensionauthtest/go.mod new file mode 100644 index 00000000000..6affcd742e5 --- /dev/null +++ b/extension/extensionauth/extensionauthtest/go.mod @@ -0,0 +1,40 @@ +module go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest + +go 1.23.0 + +require ( + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/collector/component v0.120.0 + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 + go.uber.org/goleak v1.3.0 + google.golang.org/grpc v1.70.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/extension v0.120.0 // indirect + go.opentelemetry.io/collector/pdata v1.26.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/collector/extension/extensionauth => .. + +replace go.opentelemetry.io/collector/component => ../../../component + +replace go.opentelemetry.io/collector/pdata => ../../../pdata + +replace go.opentelemetry.io/collector/extension => ../.. + +replace go.opentelemetry.io/collector/component/componenttest => ../../../component/componenttest diff --git a/extension/extensionauth/extensionauthtest/go.sum b/extension/extensionauth/extensionauthtest/go.sum new file mode 100644 index 00000000000..55710e1d6d5 --- /dev/null +++ b/extension/extensionauth/extensionauthtest/go.sum @@ -0,0 +1,88 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/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/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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.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.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= +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-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/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/extension/extensionauth/extensionauthtest/mock_clientauth.go b/extension/extensionauth/extensionauthtest/mock_clientauth.go new file mode 100644 index 00000000000..0c9bec48b20 --- /dev/null +++ b/extension/extensionauth/extensionauthtest/mock_clientauth.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package extensionauthtest // import "go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest" + +import ( + "context" + "errors" + "net/http" + + "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension/extensionauth" +) + +var ( + _ extensionauth.Client = (*MockClient)(nil) + errMockError = errors.New("mock Error") +) + +// MockClient provides a mock implementation of GRPCClient and HTTPClient interfaces +type MockClient struct { + ResultRoundTripper http.RoundTripper + ResultPerRPCCredentials credentials.PerRPCCredentials + MustError bool +} + +// Start for the MockClient does nothing +func (m *MockClient) Start(context.Context, component.Host) error { + return nil +} + +// Shutdown for the MockClient does nothing +func (m *MockClient) Shutdown(context.Context) error { + return nil +} + +// RoundTripper for the MockClient either returns error if the mock authenticator is forced to or +// returns the supplied resultRoundTripper. +func (m *MockClient) RoundTripper(http.RoundTripper) (http.RoundTripper, error) { + if m.MustError { + return nil, errMockError + } + return m.ResultRoundTripper, nil +} + +// PerRPCCredentials for the MockClient either returns error if the mock authenticator is forced to or +// returns the supplied resultPerRPCCredentials. +func (m *MockClient) PerRPCCredentials() (credentials.PerRPCCredentials, error) { + if m.MustError { + return nil, errMockError + } + return m.ResultPerRPCCredentials, nil +} diff --git a/extension/auth/authtest/mock_clientauth_test.go b/extension/extensionauth/extensionauthtest/mock_clientauth_test.go similarity index 99% rename from extension/auth/authtest/mock_clientauth_test.go rename to extension/extensionauth/extensionauthtest/mock_clientauth_test.go index d844832269d..2882bc97257 100644 --- a/extension/auth/authtest/mock_clientauth_test.go +++ b/extension/extensionauth/extensionauthtest/mock_clientauth_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package authtest +package extensionauthtest import ( "context" diff --git a/confmap/provider/fileprovider/package_test.go b/extension/extensionauth/extensionauthtest/package_test.go similarity index 87% rename from confmap/provider/fileprovider/package_test.go rename to extension/extensionauth/extensionauthtest/package_test.go index e94f125d2e3..86d97dbc452 100644 --- a/confmap/provider/fileprovider/package_test.go +++ b/extension/extensionauth/extensionauthtest/package_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package fileprovider +package extensionauthtest import ( "testing" diff --git a/extension/extensionauth/go.mod b/extension/extensionauth/go.mod new file mode 100644 index 00000000000..6090da8fd7a --- /dev/null +++ b/extension/extensionauth/go.mod @@ -0,0 +1,44 @@ +module go.opentelemetry.io/collector/extension/extensionauth + +go 1.23.0 + +require ( + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/collector/component v0.120.0 + go.opentelemetry.io/collector/component/componenttest v0.120.0 + go.opentelemetry.io/collector/extension v0.120.0 + go.uber.org/goleak v1.3.0 + google.golang.org/grpc v1.70.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/pdata v1.26.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/collector/component => ../../component + +replace go.opentelemetry.io/collector/component/componenttest => ../../component/componenttest + +replace go.opentelemetry.io/collector/extension => ../ + +replace go.opentelemetry.io/collector/pdata => ../../pdata diff --git a/extension/extensionauth/go.sum b/extension/extensionauth/go.sum new file mode 100644 index 00000000000..97b9d860e8e --- /dev/null +++ b/extension/extensionauth/go.sum @@ -0,0 +1,89 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/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/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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +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/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.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.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= +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-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/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/confmap/provider/envprovider/package_test.go b/extension/extensionauth/package_test.go similarity index 89% rename from confmap/provider/envprovider/package_test.go rename to extension/extensionauth/package_test.go index 1465f46f8af..0fd663d233a 100644 --- a/confmap/provider/envprovider/package_test.go +++ b/extension/extensionauth/package_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package envprovider +package extensionauth import ( "testing" diff --git a/extension/extensionauth/server.go b/extension/extensionauth/server.go new file mode 100644 index 00000000000..c29d1b0b466 --- /dev/null +++ b/extension/extensionauth/server.go @@ -0,0 +1,103 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package extensionauth // import "go.opentelemetry.io/collector/extension/extensionauth" + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension" +) + +// Server is an Extension that can be used as an authenticator for the configauth.Authentication option. +// Authenticators are then included as part of OpenTelemetry Collector builds and can be referenced by their +// names from the Authentication configuration. Each Server is free to define its own behavior and configuration options, +// but note that the expectations that come as part of Extensions exist here as well. For instance, multiple instances of the same +// authenticator should be possible to exist under different names. +type Server interface { + extension.Extension + + // Authenticate checks whether the given map contains valid auth data. Successfully authenticated calls will always return a nil error. + // When the authentication fails, an error must be returned and the caller must not retry. This function is typically called from interceptors, + // on behalf of receivers, but receivers can still call this directly if the usage of interceptors isn't suitable. + // The deadline and cancellation given to this function must be respected, but note that authentication data has to be part of the map, not context. + // The resulting context should contain the authentication data, such as the principal/username, group membership (if available), and the raw + // authentication data (if possible). This will allow other components in the pipeline to make decisions based on that data, such as routing based + // on tenancy as determined by the group membership, or passing through the authentication data to the next collector/backend. + // The context keys to be used are not defined yet. + Authenticate(ctx context.Context, sources map[string][]string) (context.Context, error) +} + +var _ Server = (*defaultServer)(nil) + +type defaultServer struct { + serverAuthenticateFunc ServerAuthenticateFunc + component.StartFunc + component.ShutdownFunc +} + +// Authenticate implements Server. +func (d *defaultServer) Authenticate(ctx context.Context, sources map[string][]string) (context.Context, error) { + if d.serverAuthenticateFunc == nil { + return ctx, nil + } + return d.serverAuthenticateFunc(ctx, sources) +} + +// ServerOption represents the possible options for NewServer. +type ServerOption interface { + apply(*defaultServer) +} + +type serverOptionFunc func(*defaultServer) + +func (of serverOptionFunc) apply(e *defaultServer) { + of(e) +} + +// ServerAuthenticateFunc defines the signature for the function responsible for performing the authentication based +// on the given sources map. See Server.Authenticate. +type ServerAuthenticateFunc func(ctx context.Context, sources map[string][]string) (context.Context, error) + +// Deprecated: [v0.121.0] No longer used, will be removed. +func (f ServerAuthenticateFunc) Authenticate(ctx context.Context, sources map[string][]string) (context.Context, error) { + if f == nil { + return ctx, nil + } + return f(ctx, sources) +} + +// WithServerAuthenticate specifies which function to use to perform the authentication. +func WithServerAuthenticate(authFunc ServerAuthenticateFunc) ServerOption { + return serverOptionFunc(func(o *defaultServer) { + o.serverAuthenticateFunc = authFunc + }) +} + +// WithServerStart overrides the default `Start` function for a component.Component. +// The default always returns nil. +func WithServerStart(startFunc component.StartFunc) ServerOption { + return serverOptionFunc(func(o *defaultServer) { + o.StartFunc = startFunc + }) +} + +// WithServerShutdown overrides the default `Shutdown` function for a component.Component. +// The default always returns nil. +func WithServerShutdown(shutdownFunc component.ShutdownFunc) ServerOption { + return serverOptionFunc(func(o *defaultServer) { + o.ShutdownFunc = shutdownFunc + }) +} + +// NewServer returns a Server configured with the provided options. +func NewServer(options ...ServerOption) (Server, error) { + bc := &defaultServer{} + + for _, op := range options { + op.apply(bc) + } + + return bc, nil +} diff --git a/extension/auth/server_test.go b/extension/extensionauth/server_test.go similarity index 72% rename from extension/auth/server_test.go rename to extension/extensionauth/server_test.go index 9e1f7a5b973..d43440e5967 100644 --- a/extension/auth/server_test.go +++ b/extension/extensionauth/server_test.go @@ -1,13 +1,14 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package auth +package extensionauth import ( "context" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" @@ -15,7 +16,8 @@ import ( func TestDefaultValues(t *testing.T) { // prepare - e := NewServer() + e, err := NewServer() + require.NoError(t, err) // test t.Run("start", func(t *testing.T) { @@ -38,15 +40,16 @@ func TestDefaultValues(t *testing.T) { func TestWithServerAuthenticateFunc(t *testing.T) { // prepare authCalled := false - e := NewServer( + e, err := NewServer( WithServerAuthenticate(func(ctx context.Context, _ map[string][]string) (context.Context, error) { authCalled = true return ctx, nil }), ) + require.NoError(t, err) // test - _, err := e.Authenticate(context.Background(), make(map[string][]string)) + _, err = e.Authenticate(context.Background(), make(map[string][]string)) // verify assert.True(t, authCalled) @@ -55,13 +58,14 @@ func TestWithServerAuthenticateFunc(t *testing.T) { func TestWithServerStart(t *testing.T) { called := false - e := NewServer(WithServerStart(func(context.Context, component.Host) error { + e, err := NewServer(WithServerStart(func(context.Context, component.Host) error { called = true return nil })) + require.NoError(t, err) // test - err := e.Start(context.Background(), componenttest.NewNopHost()) + err = e.Start(context.Background(), componenttest.NewNopHost()) // verify assert.True(t, called) @@ -70,13 +74,14 @@ func TestWithServerStart(t *testing.T) { func TestWithServerShutdown(t *testing.T) { called := false - e := NewServer(WithServerShutdown(func(context.Context) error { + e, err := NewServer(WithServerShutdown(func(context.Context) error { called = true return nil })) + require.NoError(t, err) // test - err := e.Shutdown(context.Background()) + err = e.Shutdown(context.Background()) // verify assert.True(t, called) diff --git a/extension/zpagesextension/go.mod b/extension/zpagesextension/go.mod index cc92a6fc1c4..540d6dc3382 100644 --- a/extension/zpagesextension/go.mod +++ b/extension/zpagesextension/go.mod @@ -44,7 +44,7 @@ require ( go.opentelemetry.io/collector/config/configcompression v1.26.0 // indirect go.opentelemetry.io/collector/config/configopaque v1.26.0 // indirect go.opentelemetry.io/collector/config/configtls v1.26.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/pdata v1.26.0 // indirect go.opentelemetry.io/collector/pipeline v0.120.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect @@ -85,7 +85,7 @@ replace go.opentelemetry.io/collector/config/configcompression => ../../config/c replace go.opentelemetry.io/collector/config/configauth => ../../config/configauth -replace go.opentelemetry.io/collector/extension/auth => ../auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../extensionauth replace go.opentelemetry.io/collector/config/confighttp => ../../config/confighttp @@ -100,4 +100,4 @@ retract ( v0.69.0 // Release failed, use v0.69.1 ) -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest diff --git a/internal/e2e/configauth_test.go b/internal/e2e/configauth_test.go new file mode 100644 index 00000000000..859b45c9367 --- /dev/null +++ b/internal/e2e/configauth_test.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/config/configauth" + "go.opentelemetry.io/collector/confmap" +) + +func TestConfmapMarshalConfigAuth(t *testing.T) { + conf := confmap.New() + require.NoError(t, conf.Marshal(configauth.Authentication{})) + assert.Equal(t, map[string]any{}, conf.ToStringMap()) +} diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index 9dcf5378d78..b1528c4941b 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -8,6 +8,7 @@ require ( go.opentelemetry.io/collector/component v0.120.0 go.opentelemetry.io/collector/component/componentstatus v0.120.0 go.opentelemetry.io/collector/component/componenttest v0.120.0 + go.opentelemetry.io/collector/config/configauth v0.120.0 go.opentelemetry.io/collector/config/configgrpc v0.120.0 go.opentelemetry.io/collector/config/confighttp v0.120.0 go.opentelemetry.io/collector/config/confignet v1.26.0 @@ -80,7 +81,6 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/client v1.26.0 // indirect - go.opentelemetry.io/collector/config/configauth v0.120.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.26.0 // indirect go.opentelemetry.io/collector/connector/xconnector v0.120.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.120.0 // indirect @@ -88,7 +88,7 @@ require ( go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 // indirect go.opentelemetry.io/collector/exporter/exporterhelper/xexporterhelper v0.120.0 // indirect go.opentelemetry.io/collector/exporter/xexporter v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/extension/extensioncapabilities v0.120.0 // indirect go.opentelemetry.io/collector/extension/extensiontest v0.120.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.120.0 // indirect @@ -102,7 +102,7 @@ require ( go.opentelemetry.io/collector/processor/xprocessor v0.120.0 // indirect go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 // indirect go.opentelemetry.io/collector/semconv v0.120.0 // indirect - go.opentelemetry.io/collector/service/hostcapabilities v0.120.0 // indirect + go.opentelemetry.io/collector/service/hostcapabilities v0.0.0-20250225223953-66e901c716a0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.9.0 // indirect go.opentelemetry.io/contrib/config v0.14.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect @@ -155,7 +155,7 @@ replace go.opentelemetry.io/collector/config/configretry => ../../config/configr replace go.opentelemetry.io/collector/config/configtls => ../../config/configtls -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/exporter/otlpexporter => ../../exporter/otlpexporter @@ -245,7 +245,7 @@ replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telem replace go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/extension/xextension => ../../extension/xextension diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 9ca3a6a75d4..c2264db2865 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -11,12 +11,12 @@ require ( github.com/google/addlicense v1.1.1 github.com/jcchavezs/porto v0.7.0 github.com/pavius/impi v0.0.3 - go.opentelemetry.io/build-tools/checkfile v0.18.0 - go.opentelemetry.io/build-tools/chloggen v0.18.0 - go.opentelemetry.io/build-tools/crosslink v0.18.0 - go.opentelemetry.io/build-tools/githubgen v0.18.0 - go.opentelemetry.io/build-tools/multimod v0.18.0 - go.opentelemetry.io/build-tools/semconvgen v0.18.0 + go.opentelemetry.io/build-tools/checkfile v0.19.0 + go.opentelemetry.io/build-tools/chloggen v0.19.0 + go.opentelemetry.io/build-tools/crosslink v0.19.0 + go.opentelemetry.io/build-tools/githubgen v0.19.0 + go.opentelemetry.io/build-tools/multimod v0.19.0 + go.opentelemetry.io/build-tools/semconvgen v0.19.0 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/tools v0.30.0 golang.org/x/vuln v1.1.4 @@ -102,7 +102,7 @@ require ( github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.8.0 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v66 v66.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect @@ -188,7 +188,7 @@ require ( github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect @@ -214,7 +214,7 @@ require ( gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.13.0 // indirect go-simpler.org/sloglint v0.9.0 // indirect - go.opentelemetry.io/build-tools v0.18.0 // indirect + go.opentelemetry.io/build-tools v0.19.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect diff --git a/internal/tools/go.sum b/internal/tools/go.sum index 0a80416ab72..deb23f4e55d 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -92,7 +92,7 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= @@ -204,8 +204,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -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/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v66 v66.0.0 h1:ADJsaXj9UotwdgK8/iFZtv7MLc8E8WBl62WLd/D/9+M= github.com/google/go-github/v66 v66.0.0/go.mod h1:+4SO9Zkuyf8ytMj0csN1NR/5OTR+MfqPp8P8dVlcvY4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -429,8 +430,8 @@ github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -505,20 +506,20 @@ go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE= go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww= -go.opentelemetry.io/build-tools v0.18.0 h1:c07DNSmJxLbgHm1wlvKCjvQTOhd1xtaWqe6+VpzrTXo= -go.opentelemetry.io/build-tools v0.18.0/go.mod h1:ZhuNyO/aAkGEFTfNhH7Nhv7fIWpxIOp8t7XshpPWiOU= -go.opentelemetry.io/build-tools/checkfile v0.18.0 h1:X9zeTrkEco0z71xrSMhRLk1BPakhALi0Uc9Qe+nUZzo= -go.opentelemetry.io/build-tools/checkfile v0.18.0/go.mod h1:hfIirAs3HMoLBhOkruPiz0HCKDGMlRW12nOMkQ0hrOI= -go.opentelemetry.io/build-tools/chloggen v0.18.0 h1:W9mHty4EJjbz742bFS0eT85aHLz85Mqpqd59tVqN3Ls= -go.opentelemetry.io/build-tools/chloggen v0.18.0/go.mod h1:Wk92v9Wsv36sXYi7hOg3ndeeLKmKBu0/kgB7wcaeqJg= -go.opentelemetry.io/build-tools/crosslink v0.18.0 h1:mWB10RvMbb8qg0/5AlySV8NJhCt9BTZRxZa82HDiWvs= -go.opentelemetry.io/build-tools/crosslink v0.18.0/go.mod h1:criIVfHTSMoyVwECMVE55VXhUVWtjpXQtwYkNK87U5g= -go.opentelemetry.io/build-tools/githubgen v0.18.0 h1:ZJHt3Tqu4bpA6nBWN6oQOhQK/QdLQSR4BITn0hf8z98= -go.opentelemetry.io/build-tools/githubgen v0.18.0/go.mod h1:UBpPXtso7exy3VU5EH1ZFfSkYQANJWO/u1lO50qdKkE= -go.opentelemetry.io/build-tools/multimod v0.18.0 h1:zHaEejwAtzUCPyQfPuaZFaIGPiD1JbZDegyEcCVNTKo= -go.opentelemetry.io/build-tools/multimod v0.18.0/go.mod h1:w9GPeYisaI+PeWXsFU9FIL/N6ULXWbI7QRbJHSSOoZw= -go.opentelemetry.io/build-tools/semconvgen v0.18.0 h1:B2JyvirBop1wfV52F/ycQGzx0Zf1Z4rfBOrOwhFUL34= -go.opentelemetry.io/build-tools/semconvgen v0.18.0/go.mod h1:gigFNRBFdaqAuaOMtJM/mF32HjWWDbf7Ucy0BDXMuOY= +go.opentelemetry.io/build-tools v0.19.0 h1:8u+3+jQFNFkKqruvMJtf5PQLx2B+iqBk9BAvzo+x7gc= +go.opentelemetry.io/build-tools v0.19.0/go.mod h1:fH3Wo56cH6GYjxZ1ajLMsyalBiFxJsYr5YBaSRO2TAI= +go.opentelemetry.io/build-tools/checkfile v0.19.0 h1:qdfIXBogCUfGyceMeM/Xhq/AZpmixUMnbf3psdpq6Qo= +go.opentelemetry.io/build-tools/checkfile v0.19.0/go.mod h1:cJr2PBIQbubYHFE9bvbRWzAAHdQdFMVdI5VD9ETycvw= +go.opentelemetry.io/build-tools/chloggen v0.19.0 h1:B3yWuOFa4THsVEL/t4Jawy4LB2oZuaWD1+ANmBGBFyM= +go.opentelemetry.io/build-tools/chloggen v0.19.0/go.mod h1:wJ8tJY5bURruxByT9gaaaGxjr1FoK8bUl/FiE+yQTfE= +go.opentelemetry.io/build-tools/crosslink v0.19.0 h1:mfYtKmzn4XstxsJWVRkYJ6M7DGIVIgtsj0LsmzyaGEM= +go.opentelemetry.io/build-tools/crosslink v0.19.0/go.mod h1:m4waJzmx/wyEm/1h1eNmZ3slghTuNKZYTZFO8OJ7si8= +go.opentelemetry.io/build-tools/githubgen v0.19.0 h1:cU+zXmIQKMpSJXjc+rgAgw3eJfXddg9Nm5sNWnv7JQ0= +go.opentelemetry.io/build-tools/githubgen v0.19.0/go.mod h1:dK/DHTlX5D1DTZ2WDURrR3yNJMo0c/ko5eXIOQHBiDk= +go.opentelemetry.io/build-tools/multimod v0.19.0 h1:P2kGUFPWIlJMZkJ6aQm1yO36Cl0SCpxX4RCjcvCUtI4= +go.opentelemetry.io/build-tools/multimod v0.19.0/go.mod h1:e+QMeo1gCugaG3LpvZnIblnkpLUzPnuxuvFzpXZQ/Gk= +go.opentelemetry.io/build-tools/semconvgen v0.19.0 h1:jPr3mObd7CSC439ySGLV3+8wJG2+NcG7dpw8Or4dn8E= +go.opentelemetry.io/build-tools/semconvgen v0.19.0/go.mod h1:yXgbrZWeC9FDFNNlCEy5eRp89fVoDNosUEDhTB1SVLw= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/otelcol/collector.go b/otelcol/collector.go index 0c3ecd9e704..29763edec65 100644 --- a/otelcol/collector.go +++ b/otelcol/collector.go @@ -72,6 +72,9 @@ type CollectorSettings struct { // ProviderModules maps provider schemes to their respective go modules. ProviderModules map[string]string + // ConverterModules maps converter names to their respective go modules. + ConverterModules []string + // LoggingOptions provides a way to change behavior of zap logging. LoggingOptions []zap.Option diff --git a/otelcol/collector_test.go b/otelcol/collector_test.go index d9559074c0e..9d1a08ec933 100644 --- a/otelcol/collector_test.go +++ b/otelcol/collector_test.go @@ -312,7 +312,7 @@ func TestCollectorStartInvalidConfig(t *testing.T) { ConfigProviderSettings: newDefaultConfigProviderSettings(t, []string{filepath.Join("testdata", "otelcol-invalid.yaml")}), }) require.NoError(t, err) - assert.Error(t, col.Run(context.Background())) + assert.EqualError(t, col.Run(context.Background()), "invalid configuration: service::pipelines::traces: references processor \"invalid\" which is not configured") } func TestNewCollectorInvalidConfigProviderSettings(t *testing.T) { @@ -595,3 +595,31 @@ func newConfFromFile(tb testing.TB, fileName string) map[string]any { return confmap.NewFromStringMap(data).ToStringMap() } + +func TestProviderAndConverterModules(t *testing.T) { + set := CollectorSettings{ + BuildInfo: component.NewDefaultBuildInfo(), + Factories: nopFactories, + ConfigProviderSettings: newDefaultConfigProviderSettings(t, []string{filepath.Join("testdata", "otelcol-nop.yaml")}), + ProviderModules: map[string]string{ + "nop": "go.opentelemetry.io/collector/confmap/provider/testprovider v1.2.3", + }, + ConverterModules: []string{ + "go.opentelemetry.io/collector/converter/testconverter v1.2.3", + }, + } + col, err := NewCollector(set) + require.NoError(t, err) + wg := startCollector(context.Background(), t, col) + require.NoError(t, err) + providerModules := map[string]string{ + "nop": "go.opentelemetry.io/collector/confmap/provider/testprovider v1.2.3", + } + converterModules := []string{ + "go.opentelemetry.io/collector/converter/testconverter v1.2.3", + } + assert.Equal(t, providerModules, col.set.ProviderModules) + assert.Equal(t, converterModules, col.set.ConverterModules) + col.Shutdown() + wg.Wait() +} diff --git a/otelcol/command_components.go b/otelcol/command_components.go index 2173aee7721..1a995a00d12 100644 --- a/otelcol/command_components.go +++ b/otelcol/command_components.go @@ -25,7 +25,7 @@ type componentWithStability struct { } type componentWithoutStability struct { - Scheme string + Scheme string `yaml:",omitempty"` Module string } @@ -37,6 +37,7 @@ type componentsOutput struct { Connectors []componentWithStability Extensions []componentWithStability Providers []componentWithoutStability + Converters []componentWithoutStability `yaml:",omitempty"` } // newComponentsCommand constructs a new components command using the given CollectorSettings. @@ -123,6 +124,12 @@ func newComponentsCommand(set CollectorSettings) *cobra.Command { }) } + for _, converterModule := range set.ConverterModules { + components.Converters = append(components.Converters, componentWithoutStability{ + Module: converterModule, + }) + } + yamlData, err := yaml.Marshal(components) if err != nil { return err diff --git a/otelcol/command_components_test.go b/otelcol/command_components_test.go index 5b429dd3f06..c88c9ebe608 100644 --- a/otelcol/command_components_test.go +++ b/otelcol/command_components_test.go @@ -24,6 +24,9 @@ func TestNewBuildSubCommand(t *testing.T) { ProviderModules: map[string]string{ "nop": "go.opentelemetry.io/collector/confmap/provider/testprovider v1.2.3", }, + ConverterModules: []string{ + "go.opentelemetry.io/collector/converter/testconverter v1.2.3", + }, } cmd := NewCommand(set) cmd.SetArgs([]string{"components"}) diff --git a/otelcol/go.mod b/otelcol/go.mod index 892056c87a1..e21781b0585 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -90,7 +90,7 @@ require ( go.opentelemetry.io/collector/processor/xprocessor v0.120.0 // indirect go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 // indirect go.opentelemetry.io/collector/semconv v0.120.0 // indirect - go.opentelemetry.io/collector/service/hostcapabilities v0.120.0 // indirect + go.opentelemetry.io/collector/service/hostcapabilities v0.0.0-20250225223953-66e901c716a0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.9.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.34.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect @@ -168,7 +168,7 @@ replace go.opentelemetry.io/collector/config/confighttp => ../config/confighttp replace go.opentelemetry.io/collector/config/configauth => ../config/configauth -replace go.opentelemetry.io/collector/extension/auth => ../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../extension/extensionauth replace go.opentelemetry.io/collector/config/configcompression => ../config/configcompression @@ -210,7 +210,7 @@ replace go.opentelemetry.io/collector/internal/fanoutconsumer => ../internal/fan replace go.opentelemetry.io/collector/extension/extensiontest => ../extension/extensiontest -replace go.opentelemetry.io/collector/extension/auth/authtest => ../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/extension/xextension => ../extension/xextension diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index 7e5c870e581..09188b6a561 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -86,7 +86,7 @@ require ( go.opentelemetry.io/collector/receiver v0.120.0 // indirect go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 // indirect go.opentelemetry.io/collector/semconv v0.120.0 // indirect - go.opentelemetry.io/collector/service/hostcapabilities v0.120.0 // indirect + go.opentelemetry.io/collector/service/hostcapabilities v0.0.0-20250225223953-66e901c716a0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.9.0 // indirect go.opentelemetry.io/contrib/config v0.14.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.34.0 // indirect @@ -201,7 +201,7 @@ replace go.opentelemetry.io/collector/config/confighttp => ../../config/confight replace go.opentelemetry.io/collector/client => ../../client -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/otelcol => ../ @@ -215,7 +215,7 @@ replace go.opentelemetry.io/collector/internal/fanoutconsumer => ../../internal/ replace go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/extension/xextension => ../../extension/xextension diff --git a/otelcol/testdata/components-output.yaml b/otelcol/testdata/components-output.yaml index 6459e69cde8..94e1797f3da 100644 --- a/otelcol/testdata/components-output.yaml +++ b/otelcol/testdata/components-output.yaml @@ -44,3 +44,5 @@ extensions: providers: - scheme: nop module: go.opentelemetry.io/collector/confmap/provider/testprovider v1.2.3 +converters: + - module: go.opentelemetry.io/collector/converter/testconverter v1.2.3 diff --git a/pdata/internal/cmd/pdatagen/internal/pprofile_package.go b/pdata/internal/cmd/pdatagen/internal/pprofile_package.go index 146743f7935..aca1593a038 100644 --- a/pdata/internal/cmd/pdatagen/internal/pprofile_package.go +++ b/pdata/internal/cmd/pdatagen/internal/pprofile_package.go @@ -325,6 +325,12 @@ var sample = &messageValueStruct{ fieldName: "AttributeIndices", returnSlice: int32Slice, }, + &optionalPrimitiveValue{ + fieldName: "LinkIndex", + returnType: "int32", + defaultVal: "int32(0)", + testVal: "int32(1)", + }, &sliceField{ fieldName: "TimestampsUnixNano", returnSlice: uInt64Slice, diff --git a/pdata/plog/pb.go b/pdata/plog/pb.go index bb102591bf2..a4cb09eb6ea 100644 --- a/pdata/plog/pb.go +++ b/pdata/plog/pb.go @@ -22,6 +22,18 @@ func (e *ProtoMarshaler) LogsSize(ld Logs) int { return pb.Size() } +func (e *ProtoMarshaler) ResourceLogsSize(rl ResourceLogs) int { + return rl.orig.Size() +} + +func (e *ProtoMarshaler) ScopeLogsSize(sl ScopeLogs) int { + return sl.orig.Size() +} + +func (e *ProtoMarshaler) LogRecordSize(lr LogRecord) int { + return lr.orig.Size() +} + var _ Unmarshaler = (*ProtoUnmarshaler)(nil) type ProtoUnmarshaler struct{} diff --git a/pdata/pprofile/generated_sample.go b/pdata/pprofile/generated_sample.go index c62027753d2..44138f9f47b 100644 --- a/pdata/pprofile/generated_sample.go +++ b/pdata/pprofile/generated_sample.go @@ -78,6 +78,29 @@ func (ms Sample) AttributeIndices() pcommon.Int32Slice { return pcommon.Int32Slice(internal.NewInt32Slice(&ms.orig.AttributeIndices, ms.state)) } +// LinkIndex returns the linkindex associated with this Sample. +func (ms Sample) LinkIndex() int32 { + return ms.orig.GetLinkIndex() +} + +// HasLinkIndex returns true if the Sample contains a +// LinkIndex value, false otherwise. +func (ms Sample) HasLinkIndex() bool { + return ms.orig.LinkIndex_ != nil +} + +// SetLinkIndex replaces the linkindex associated with this Sample. +func (ms Sample) SetLinkIndex(v int32) { + ms.state.AssertMutable() + ms.orig.LinkIndex_ = &otlpprofiles.Sample_LinkIndex{LinkIndex: v} +} + +// RemoveLinkIndex removes the linkindex associated with this Sample. +func (ms Sample) RemoveLinkIndex() { + ms.state.AssertMutable() + ms.orig.LinkIndex_ = nil +} + // TimestampsUnixNano returns the TimestampsUnixNano associated with this Sample. func (ms Sample) TimestampsUnixNano() pcommon.UInt64Slice { return pcommon.UInt64Slice(internal.NewUInt64Slice(&ms.orig.TimestampsUnixNano, ms.state)) @@ -90,5 +113,9 @@ func (ms Sample) CopyTo(dest Sample) { dest.SetLocationsLength(ms.LocationsLength()) ms.Value().CopyTo(dest.Value()) ms.AttributeIndices().CopyTo(dest.AttributeIndices()) + if ms.HasLinkIndex() { + dest.SetLinkIndex(ms.LinkIndex()) + } + ms.TimestampsUnixNano().CopyTo(dest.TimestampsUnixNano()) } diff --git a/pdata/pprofile/generated_sample_test.go b/pdata/pprofile/generated_sample_test.go index 3c535f3c658..b0e7579e630 100644 --- a/pdata/pprofile/generated_sample_test.go +++ b/pdata/pprofile/generated_sample_test.go @@ -71,6 +71,16 @@ func TestSample_AttributeIndices(t *testing.T) { assert.Equal(t, pcommon.Int32Slice(internal.GenerateTestInt32Slice()), ms.AttributeIndices()) } +func TestSample_LinkIndex(t *testing.T) { + ms := NewSample() + assert.Equal(t, int32(0), ms.LinkIndex()) + ms.SetLinkIndex(int32(1)) + assert.True(t, ms.HasLinkIndex()) + assert.Equal(t, int32(1), ms.LinkIndex()) + ms.RemoveLinkIndex() + assert.False(t, ms.HasLinkIndex()) +} + func TestSample_TimestampsUnixNano(t *testing.T) { ms := NewSample() assert.Equal(t, pcommon.NewUInt64Slice(), ms.TimestampsUnixNano()) @@ -89,5 +99,6 @@ func fillTestSample(tv Sample) { tv.orig.LocationsLength = int32(1) internal.FillTestInt64Slice(internal.NewInt64Slice(&tv.orig.Value, tv.state)) internal.FillTestInt32Slice(internal.NewInt32Slice(&tv.orig.AttributeIndices, tv.state)) + tv.orig.LinkIndex_ = &otlpprofiles.Sample_LinkIndex{LinkIndex: int32(1)} internal.FillTestUInt64Slice(internal.NewUInt64Slice(&tv.orig.TimestampsUnixNano, tv.state)) } diff --git a/processor/batchprocessor/batch_processor_test.go b/processor/batchprocessor/batch_processor_test.go index d1321c1a7a5..61af21f10ec 100644 --- a/processor/batchprocessor/batch_processor_test.go +++ b/processor/batchprocessor/batch_processor_test.go @@ -27,6 +27,7 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/pdata/testdata" + "go.opentelemetry.io/collector/processor/batchprocessor/internal/metadata" "go.opentelemetry.io/collector/processor/batchprocessor/internal/metadatatest" "go.opentelemetry.io/collector/processor/processortest" ) @@ -35,7 +36,7 @@ func TestProcessorShutdown(t *testing.T) { factory := NewFactory() ctx := context.Background() - processorCreationSet := processortest.NewNopSettings(processortest.NopType) + processorCreationSet := processortest.NewNopSettings(metadata.Type) for i := 0; i < 5; i++ { require.NotPanics(t, func() { @@ -62,7 +63,7 @@ func TestProcessorLifecycle(t *testing.T) { factory := NewFactory() ctx := context.Background() - processorCreationSet := processortest.NewNopSettings(processortest.NopType) + processorCreationSet := processortest.NewNopSettings(metadata.Type) for i := 0; i < 5; i++ { tProc, err := factory.CreateTraces(ctx, processorCreationSet, factory.CreateDefaultConfig(), consumertest.NewNop()) @@ -86,7 +87,7 @@ func TestBatchProcessorSpansDelivered(t *testing.T) { sink := new(consumertest.TracesSink) cfg := createDefaultConfig().(*Config) cfg.SendBatchSize = 128 - traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) @@ -127,7 +128,7 @@ func TestBatchProcessorSpansDeliveredEnforceBatchSize(t *testing.T) { cfg := createDefaultConfig().(*Config) cfg.SendBatchSize = 128 cfg.SendBatchMaxSize = 130 - traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) @@ -368,7 +369,7 @@ func TestBatchProcessorSentByTimeout(t *testing.T) { spansPerRequest := 10 start := time.Now() - traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) @@ -413,7 +414,7 @@ func TestBatchProcessorTraceSendWhenClosing(t *testing.T) { } sink := new(consumertest.TracesSink) - traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) @@ -442,7 +443,7 @@ func TestBatchMetricProcessor_ReceivingData(t *testing.T) { metricsPerRequest := 5 sink := new(consumertest.MetricsSink) - metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, metrics.Start(context.Background(), componenttest.NewNopHost())) @@ -603,7 +604,7 @@ func TestBatchMetricsProcessor_Timeout(t *testing.T) { metricsPerRequest := 10 sink := new(consumertest.MetricsSink) - metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, metrics.Start(context.Background(), componenttest.NewNopHost())) @@ -650,7 +651,7 @@ func TestBatchMetricProcessor_Shutdown(t *testing.T) { metricsPerRequest := 10 sink := new(consumertest.MetricsSink) - metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, metrics.Start(context.Background(), componenttest.NewNopHost())) @@ -746,7 +747,7 @@ func BenchmarkMultiBatchMetricProcessor(b *testing.B) { func runMetricsProcessorBenchmark(b *testing.B, cfg *Config) { ctx := context.Background() sink := new(metricsSink) - metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + metrics, err := NewFactory().CreateMetrics(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(b, err) require.NoError(b, metrics.Start(ctx, componenttest.NewNopHost())) @@ -792,7 +793,7 @@ func TestBatchLogProcessor_ReceivingData(t *testing.T) { logsPerRequest := 5 sink := new(consumertest.LogsSink) - logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, logs.Start(context.Background(), componenttest.NewNopHost())) @@ -931,7 +932,7 @@ func TestBatchLogsProcessor_Timeout(t *testing.T) { logsPerRequest := 10 sink := new(consumertest.LogsSink) - logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, logs.Start(context.Background(), componenttest.NewNopHost())) @@ -978,7 +979,7 @@ func TestBatchLogProcessor_Shutdown(t *testing.T) { logsPerRequest := 10 sink := new(consumertest.LogsSink) - logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, logs.Start(context.Background(), componenttest.NewNopHost())) @@ -1055,7 +1056,7 @@ func TestBatchProcessorSpansBatchedByMetadata(t *testing.T) { cfg.SendBatchSize = 1000 cfg.Timeout = 10 * time.Minute cfg.MetadataKeys = []string{"token1", "token2"} - traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) @@ -1146,7 +1147,7 @@ func TestBatchProcessorMetadataCardinalityLimit(t *testing.T) { cfg := createDefaultConfig().(*Config) cfg.MetadataKeys = []string{"token"} cfg.MetadataCardinalityLimit = cardLimit - traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + traces, err := NewFactory().CreateTraces(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) @@ -1187,7 +1188,7 @@ func TestBatchZeroConfig(t *testing.T) { const requestCount = 5 const logsPerRequest = 10 sink := new(consumertest.LogsSink) - logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, logs.Start(context.Background(), componenttest.NewNopHost())) defer func() { require.NoError(t, logs.Shutdown(context.Background())) }() @@ -1226,7 +1227,7 @@ func TestBatchSplitOnly(t *testing.T) { require.NoError(t, cfg.Validate()) sink := new(consumertest.LogsSink) - logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(processortest.NopType), cfg, sink) + logs, err := NewFactory().CreateLogs(context.Background(), processortest.NewNopSettings(metadata.Type), cfg, sink) require.NoError(t, err) require.NoError(t, logs.Start(context.Background(), componenttest.NewNopHost())) defer func() { require.NoError(t, logs.Shutdown(context.Background())) }() diff --git a/processor/internal/err.go b/processor/internal/err.go new file mode 100644 index 00000000000..aa412bba43c --- /dev/null +++ b/processor/internal/err.go @@ -0,0 +1,14 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "go.opentelemetry.io/collector/processor/internal" + +import ( + "fmt" + + "go.opentelemetry.io/collector/component" +) + +func ErrIDMismatch(id component.ID, typ component.Type) error { + return fmt.Errorf("component type mismatch: component ID %q does not have type %q", id, typ) +} diff --git a/processor/memorylimiterprocessor/README.md b/processor/memorylimiterprocessor/README.md index 661d86508c1..492f0e1d73b 100644 --- a/processor/memorylimiterprocessor/README.md +++ b/processor/memorylimiterprocessor/README.md @@ -3,10 +3,12 @@ | Status | | | ------------- |-----------| -| Stability | [beta]: traces, metrics, logs | +| Stability | [alpha]: profiles | +| | [beta]: traces, metrics, logs | | Distributions | [core], [contrib], [k8s] | | Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprocessor%2Fmemorylimiter%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprocessor%2Fmemorylimiter) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprocessor%2Fmemorylimiter%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprocessor%2Fmemorylimiter) | +[alpha]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#alpha [beta]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#beta [core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol [contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib diff --git a/processor/memorylimiterprocessor/factory.go b/processor/memorylimiterprocessor/factory.go index 39ae9bf782a..59c95cec9bf 100644 --- a/processor/memorylimiterprocessor/factory.go +++ b/processor/memorylimiterprocessor/factory.go @@ -11,12 +11,15 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/consumer/xconsumer" "go.opentelemetry.io/collector/internal/memorylimiter" "go.opentelemetry.io/collector/internal/telemetry" "go.opentelemetry.io/collector/internal/telemetry/componentattribute" "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal/metadata" "go.opentelemetry.io/collector/processor/processorhelper" + "go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper" + "go.opentelemetry.io/collector/processor/xprocessor" ) var processorCapabilities = consumer.Capabilities{MutatesData: false} @@ -29,16 +32,17 @@ type factory struct { } // NewFactory returns a new factory for the Memory Limiter processor. -func NewFactory() processor.Factory { +func NewFactory() xprocessor.Factory { f := &factory{ memoryLimiters: map[component.Config]*memoryLimiterProcessor{}, } - return processor.NewFactory( + return xprocessor.NewFactory( metadata.Type, createDefaultConfig, - processor.WithTraces(f.createTraces, metadata.TracesStability), - processor.WithMetrics(f.createMetrics, metadata.MetricsStability), - processor.WithLogs(f.createLogs, metadata.LogsStability)) + xprocessor.WithTraces(f.createTraces, metadata.TracesStability), + xprocessor.WithMetrics(f.createMetrics, metadata.MetricsStability), + xprocessor.WithLogs(f.createLogs, metadata.LogsStability), + xprocessor.WithProfiles(f.createProfiles, metadata.ProfilesStability)) } // CreateDefaultConfig creates the default configuration for processor. Notice @@ -98,6 +102,29 @@ func (f *factory) createLogs( processorhelper.WithShutdown(memLimiter.shutdown)) } +func (f *factory) createProfiles( + ctx context.Context, + set processor.Settings, + cfg component.Config, + nextConsumer xconsumer.Profiles, +) (xprocessor.Profiles, error) { + memLimiter, err := f.getMemoryLimiter(set, cfg) + if err != nil { + return nil, err + } + + return xprocessorhelper.NewProfiles( + ctx, + set, + cfg, + nextConsumer, + memLimiter.processProfiles, + xprocessorhelper.WithCapabilities(processorCapabilities), + xprocessorhelper.WithStart(memLimiter.start), + xprocessorhelper.WithShutdown(memLimiter.shutdown), + ) +} + // getMemoryLimiter checks if we have a cached memoryLimiter with a specific config, // otherwise initialize and add one to the store. func (f *factory) getMemoryLimiter(set processor.Settings, cfg component.Config) (*memoryLimiterProcessor, error) { diff --git a/processor/memorylimiterprocessor/factory_test.go b/processor/memorylimiterprocessor/factory_test.go index d824560271d..dba8615dada 100644 --- a/processor/memorylimiterprocessor/factory_test.go +++ b/processor/memorylimiterprocessor/factory_test.go @@ -68,11 +68,17 @@ func TestCreateProcessor(t *testing.T) { lp, err := factory.CreateLogs(context.Background(), set, cfg, consumertest.NewNop()) require.NoError(t, err) assert.NotNil(t, lp) - assert.NoError(t, lp.Start(context.Background(), componenttest.NewNopHost())) + require.NoError(t, lp.Start(context.Background(), componenttest.NewNopHost())) + + pp, err := factory.CreateProfiles(context.Background(), set, cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NotNil(t, pp) + assert.NoError(t, pp.Start(context.Background(), componenttest.NewNopHost())) assert.NoError(t, lp.Shutdown(context.Background())) assert.NoError(t, tp.Shutdown(context.Background())) assert.NoError(t, mp.Shutdown(context.Background())) + assert.NoError(t, pp.Shutdown(context.Background())) // verify that no monitoring routine is running require.ErrorIs(t, tp.Shutdown(context.Background()), memorylimiter.ErrShutdownNotStarted) diff --git a/processor/memorylimiterprocessor/go.mod b/processor/memorylimiterprocessor/go.mod index 1dafa679120..cf6792008a3 100644 --- a/processor/memorylimiterprocessor/go.mod +++ b/processor/memorylimiterprocessor/go.mod @@ -10,12 +10,17 @@ require ( go.opentelemetry.io/collector/consumer v1.26.0 go.opentelemetry.io/collector/consumer/consumererror v0.120.0 go.opentelemetry.io/collector/consumer/consumertest v0.120.0 + go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 go.opentelemetry.io/collector/internal/memorylimiter v0.120.0 go.opentelemetry.io/collector/internal/telemetry v0.120.0 go.opentelemetry.io/collector/pdata v1.26.0 + go.opentelemetry.io/collector/pdata/pprofile v0.120.0 go.opentelemetry.io/collector/pipeline v0.120.0 + go.opentelemetry.io/collector/pipeline/xpipeline v0.120.0 go.opentelemetry.io/collector/processor v0.120.0 + go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper v0.120.0 go.opentelemetry.io/collector/processor/processortest v0.120.0 + go.opentelemetry.io/collector/processor/xprocessor v0.120.0 go.opentelemetry.io/otel v1.34.0 go.opentelemetry.io/otel/metric v1.34.0 go.opentelemetry.io/otel/sdk/metric v1.34.0 @@ -50,10 +55,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/component/componentstatus v0.120.0 // indirect - go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 // indirect - go.opentelemetry.io/collector/pdata/pprofile v0.120.0 // indirect go.opentelemetry.io/collector/pdata/testdata v0.120.0 // indirect - go.opentelemetry.io/collector/processor/xprocessor v0.120.0 // indirect go.opentelemetry.io/otel/sdk v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.33.0 // indirect @@ -103,3 +105,7 @@ replace go.opentelemetry.io/collector/internal/memorylimiter => ../../internal/m replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry replace go.opentelemetry.io/collector/consumer/consumererror => ../../consumer/consumererror + +replace go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper => ../processorhelper/xprocessorhelper + +replace go.opentelemetry.io/collector/pipeline/xpipeline => ../../pipeline/xpipeline diff --git a/processor/memorylimiterprocessor/internal/metadata/generated_status.go b/processor/memorylimiterprocessor/internal/metadata/generated_status.go index 0e841608278..e22c704f519 100644 --- a/processor/memorylimiterprocessor/internal/metadata/generated_status.go +++ b/processor/memorylimiterprocessor/internal/metadata/generated_status.go @@ -12,7 +12,8 @@ var ( ) const ( - TracesStability = component.StabilityLevelBeta - MetricsStability = component.StabilityLevelBeta - LogsStability = component.StabilityLevelBeta + ProfilesStability = component.StabilityLevelAlpha + TracesStability = component.StabilityLevelBeta + MetricsStability = component.StabilityLevelBeta + LogsStability = component.StabilityLevelBeta ) diff --git a/processor/memorylimiterprocessor/memorylimiter.go b/processor/memorylimiterprocessor/memorylimiter.go index 45da42e0b32..fbb9f81bf32 100644 --- a/processor/memorylimiterprocessor/memorylimiter.go +++ b/processor/memorylimiterprocessor/memorylimiter.go @@ -10,8 +10,10 @@ import ( "go.opentelemetry.io/collector/internal/memorylimiter" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/pprofile" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/pipeline" + "go.opentelemetry.io/collector/pipeline/xpipeline" "go.opentelemetry.io/collector/processor" ) @@ -50,11 +52,8 @@ func (p *memoryLimiterProcessor) shutdown(ctx context.Context) error { func (p *memoryLimiterProcessor) processTraces(ctx context.Context, td ptrace.Traces) (ptrace.Traces, error) { numSpans := td.SpanCount() if p.memlimiter.MustRefuse() { - // TODO: actually to be 100% sure that this is "refused" and not "dropped" - // it is necessary to check the pipeline to see if this is directly connected - // to a receiver (ie.: a receiver is on the call stack). For now it - // assumes that the pipeline is properly configured and a receiver is on the - // callstack and that the receiver will correctly retry the refused data again. + // TODO: + // https://github.com/open-telemetry/opentelemetry-collector/issues/12463 p.obsrep.refused(ctx, numSpans, pipeline.SignalTraces) return td, memorylimiter.ErrDataRefused } @@ -68,11 +67,8 @@ func (p *memoryLimiterProcessor) processTraces(ctx context.Context, td ptrace.Tr func (p *memoryLimiterProcessor) processMetrics(ctx context.Context, md pmetric.Metrics) (pmetric.Metrics, error) { numDataPoints := md.DataPointCount() if p.memlimiter.MustRefuse() { - // TODO: actually to be 100% sure that this is "refused" and not "dropped" - // it is necessary to check the pipeline to see if this is directly connected - // to a receiver (ie.: a receiver is on the call stack). For now it - // assumes that the pipeline is properly configured and a receiver is on the - // callstack. + // TODO: + // https://github.com/open-telemetry/opentelemetry-collector/issues/12463 p.obsrep.refused(ctx, numDataPoints, pipeline.SignalMetrics) return md, memorylimiter.ErrDataRefused } @@ -86,11 +82,8 @@ func (p *memoryLimiterProcessor) processMetrics(ctx context.Context, md pmetric. func (p *memoryLimiterProcessor) processLogs(ctx context.Context, ld plog.Logs) (plog.Logs, error) { numRecords := ld.LogRecordCount() if p.memlimiter.MustRefuse() { - // TODO: actually to be 100% sure that this is "refused" and not "dropped" - // it is necessary to check the pipeline to see if this is directly connected - // to a receiver (ie.: a receiver is on the call stack). For now it - // assumes that the pipeline is properly configured and a receiver is on the - // callstack. + // TODO: + // https://github.com/open-telemetry/opentelemetry-collector/issues/12463 p.obsrep.refused(ctx, numRecords, pipeline.SignalLogs) return ld, memorylimiter.ErrDataRefused } @@ -100,3 +93,18 @@ func (p *memoryLimiterProcessor) processLogs(ctx context.Context, ld plog.Logs) p.obsrep.accepted(ctx, numRecords, pipeline.SignalLogs) return ld, nil } + +func (p *memoryLimiterProcessor) processProfiles(ctx context.Context, td pprofile.Profiles) (pprofile.Profiles, error) { + numProfiles := td.SampleCount() + if p.memlimiter.MustRefuse() { + // TODO: + // https://github.com/open-telemetry/opentelemetry-collector/issues/12463 + p.obsrep.refused(ctx, numProfiles, xpipeline.SignalProfiles) + return td, memorylimiter.ErrDataRefused + } + + // Even if the next consumer returns error record the data as accepted by + // this processor. + p.obsrep.accepted(ctx, numProfiles, xpipeline.SignalProfiles) + return td, nil +} diff --git a/processor/memorylimiterprocessor/memorylimiter_test.go b/processor/memorylimiterprocessor/memorylimiter_test.go index 85a7fdf4fcb..8b542952c88 100644 --- a/processor/memorylimiterprocessor/memorylimiter_test.go +++ b/processor/memorylimiterprocessor/memorylimiter_test.go @@ -22,12 +22,14 @@ import ( "go.opentelemetry.io/collector/internal/memorylimiter/iruntime" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/pprofile" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal" "go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal/metadata" "go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal/metadatatest" "go.opentelemetry.io/collector/processor/processorhelper" + "go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper" "go.opentelemetry.io/collector/processor/processortest" ) @@ -419,6 +421,96 @@ func TestLogMemoryPressureResponse(t *testing.T) { }) } +// TestProfileMemoryPressureResponse manipulates results from querying memory and +// check expected side effects. +func TestProfileMemoryPressureResponse(t *testing.T) { + pd := pprofile.NewProfiles() + ctx := context.Background() + + tests := []struct { + name string + mlCfg *Config + memAlloc uint64 + expectError bool + }{ + { + name: "Below memAllocLimit", + mlCfg: &Config{ + CheckInterval: time.Second, + MemoryLimitPercentage: 50, + MemorySpikePercentage: 1, + }, + memAlloc: 800, + expectError: false, + }, + { + name: "Above memAllocLimit", + mlCfg: &Config{ + CheckInterval: time.Second, + MemoryLimitPercentage: 50, + MemorySpikePercentage: 1, + }, + memAlloc: 1800, + expectError: true, + }, + { + name: "Below memSpikeLimit", + mlCfg: &Config{ + CheckInterval: time.Second, + MemoryLimitPercentage: 50, + MemorySpikePercentage: 10, + }, + memAlloc: 800, + expectError: false, + }, + { + name: "Above memSpikeLimit", + mlCfg: &Config{ + CheckInterval: time.Second, + MemoryLimitPercentage: 50, + MemorySpikePercentage: 11, + }, + memAlloc: 800, + expectError: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + memorylimiter.GetMemoryFn = totalMemory + memorylimiter.ReadMemStatsFn = func(ms *runtime.MemStats) { + ms.Alloc = tt.memAlloc + } + + ml, err := newMemoryLimiterProcessor(processortest.NewNopSettings(metadata.Type), tt.mlCfg) + require.NoError(t, err) + tp, err := xprocessorhelper.NewProfiles( + context.Background(), + processortest.NewNopSettings(metadata.Type), + tt.mlCfg, + consumertest.NewNop(), + ml.processProfiles, + xprocessorhelper.WithCapabilities(processorCapabilities), + xprocessorhelper.WithStart(ml.start), + xprocessorhelper.WithShutdown(ml.shutdown)) + require.NoError(t, err) + + assert.NoError(t, tp.Start(ctx, &host{})) + ml.memlimiter.CheckMemLimits() + err = tp.ConsumeProfiles(ctx, pd) + if tt.expectError { + assert.Equal(t, memorylimiter.ErrDataRefused, err) + } else { + require.NoError(t, err) + } + assert.NoError(t, tp.Shutdown(ctx)) + }) + } + t.Cleanup(func() { + memorylimiter.GetMemoryFn = iruntime.TotalMemory + memorylimiter.ReadMemStatsFn = runtime.ReadMemStats + }) +} + type host struct { component.Host } diff --git a/processor/memorylimiterprocessor/metadata.yaml b/processor/memorylimiterprocessor/metadata.yaml index d0fb4dfc6eb..870b22863fa 100644 --- a/processor/memorylimiterprocessor/metadata.yaml +++ b/processor/memorylimiterprocessor/metadata.yaml @@ -4,6 +4,7 @@ github_project: open-telemetry/opentelemetry-collector status: class: processor stability: + alpha: [profiles] beta: [traces, metrics, logs] distributions: [core, contrib, k8s] diff --git a/processor/processor.go b/processor/processor.go index 06d2239ec58..c54154fe9f2 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/pipeline" + "go.opentelemetry.io/collector/processor/internal" ) // Traces is a processor that can consume traces. @@ -125,6 +126,11 @@ func (f *factory) CreateTraces(ctx context.Context, set Settings, cfg component. if f.createTracesFunc == nil { return nil, pipeline.ErrSignalNotSupported } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createTracesFunc(ctx, set, cfg, next) } @@ -132,6 +138,11 @@ func (f *factory) CreateMetrics(ctx context.Context, set Settings, cfg component if f.createMetricsFunc == nil { return nil, pipeline.ErrSignalNotSupported } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createMetricsFunc(ctx, set, cfg, next) } @@ -139,6 +150,11 @@ func (f *factory) CreateLogs(ctx context.Context, set Settings, cfg component.Co if f.createLogsFunc == nil { return nil, pipeline.ErrSignalNotSupported } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createLogsFunc(ctx, set, cfg, next) } diff --git a/processor/processor_test.go b/processor/processor_test.go index 3d3ea5dafe8..14bd5e6a2e5 100644 --- a/processor/processor_test.go +++ b/processor/processor_test.go @@ -14,26 +14,30 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/pipeline" + "go.opentelemetry.io/collector/processor/internal" +) + +var ( + testType = component.MustNewType("test") + testID = component.NewID(testType) ) func TestNewFactory(t *testing.T) { - testType := component.MustNewType("test") defaultCfg := struct{}{} f := NewFactory( testType, func() component.Config { return &defaultCfg }) assert.EqualValues(t, testType, f.Type()) assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig()) - _, err := f.CreateTraces(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err := f.CreateTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) - _, err = f.CreateMetrics(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = f.CreateMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) - _, err = f.CreateLogs(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = f.CreateLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) } func TestNewFactoryWithOptions(t *testing.T) { - testType := component.MustNewType("test") defaultCfg := struct{}{} f := NewFactory( testType, @@ -44,17 +48,26 @@ func TestNewFactoryWithOptions(t *testing.T) { assert.EqualValues(t, testType, f.Type()) assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig()) + wrongID := component.MustNewID("wrong") + wrongIDErrStr := internal.ErrIDMismatch(wrongID, testType).Error() + assert.Equal(t, component.StabilityLevelAlpha, f.TracesStability()) - _, err := f.CreateTraces(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err := f.CreateTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) + _, err = f.CreateTraces(context.Background(), Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + require.EqualError(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelBeta, f.MetricsStability()) - _, err = f.CreateMetrics(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = f.CreateMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.NoError(t, err) + _, err = f.CreateMetrics(context.Background(), Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + require.EqualError(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelUnmaintained, f.LogsStability()) - _, err = f.CreateLogs(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) - assert.NoError(t, err) + _, err = f.CreateLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) + require.NoError(t, err) + _, err = f.CreateLogs(context.Background(), Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + require.EqualError(t, err, wrongIDErrStr) } var nopInstance = &nopProcessor{ diff --git a/processor/processortest/shutdown_verifier.go b/processor/processortest/shutdown_verifier.go index c579ecc3cac..dde956de35e 100644 --- a/processor/processortest/shutdown_verifier.go +++ b/processor/processortest/shutdown_verifier.go @@ -22,7 +22,7 @@ import ( func verifyTracesDoesNotProduceAfterShutdown(t *testing.T, factory processor.Factory, cfg component.Config) { // Create a proc and output its produce to a sink. nextSink := new(consumertest.TracesSink) - proc, err := factory.CreateTraces(context.Background(), NewNopSettings(NopType), cfg, nextSink) + proc, err := factory.CreateTraces(context.Background(), NewNopSettings(factory.Type()), cfg, nextSink) if errors.Is(err, pipeline.ErrSignalNotSupported) { return } @@ -46,7 +46,7 @@ func verifyTracesDoesNotProduceAfterShutdown(t *testing.T, factory processor.Fac func verifyLogsDoesNotProduceAfterShutdown(t *testing.T, factory processor.Factory, cfg component.Config) { // Create a proc and output its produce to a sink. nextSink := new(consumertest.LogsSink) - proc, err := factory.CreateLogs(context.Background(), NewNopSettings(NopType), cfg, nextSink) + proc, err := factory.CreateLogs(context.Background(), NewNopSettings(factory.Type()), cfg, nextSink) if errors.Is(err, pipeline.ErrSignalNotSupported) { return } @@ -70,7 +70,7 @@ func verifyLogsDoesNotProduceAfterShutdown(t *testing.T, factory processor.Facto func verifyMetricsDoesNotProduceAfterShutdown(t *testing.T, factory processor.Factory, cfg component.Config) { // Create a proc and output its produce to a sink. nextSink := new(consumertest.MetricsSink) - proc, err := factory.CreateMetrics(context.Background(), NewNopSettings(NopType), cfg, nextSink) + proc, err := factory.CreateMetrics(context.Background(), NewNopSettings(factory.Type()), cfg, nextSink) if errors.Is(err, pipeline.ErrSignalNotSupported) { return } diff --git a/processor/processortest/unhealthy_processor_test.go b/processor/processortest/unhealthy_processor_test.go index 4398d55bae2..828373af268 100644 --- a/processor/processortest/unhealthy_processor_test.go +++ b/processor/processortest/unhealthy_processor_test.go @@ -26,21 +26,21 @@ func TestNewUnhealthyProcessorFactory(t *testing.T) { cfg := factory.CreateDefaultConfig() assert.Equal(t, &struct{}{}, cfg) - traces, err := factory.CreateTraces(context.Background(), NewNopSettings(NopType), cfg, consumertest.NewNop()) + traces, err := factory.CreateTraces(context.Background(), NewNopSettings(factory.Type()), cfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, consumer.Capabilities{MutatesData: false}, traces.Capabilities()) assert.NoError(t, traces.Start(context.Background(), componenttest.NewNopHost())) assert.NoError(t, traces.ConsumeTraces(context.Background(), ptrace.NewTraces())) assert.NoError(t, traces.Shutdown(context.Background())) - metrics, err := factory.CreateMetrics(context.Background(), NewNopSettings(NopType), cfg, consumertest.NewNop()) + metrics, err := factory.CreateMetrics(context.Background(), NewNopSettings(factory.Type()), cfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, consumer.Capabilities{MutatesData: false}, metrics.Capabilities()) assert.NoError(t, metrics.Start(context.Background(), componenttest.NewNopHost())) assert.NoError(t, metrics.ConsumeMetrics(context.Background(), pmetric.NewMetrics())) assert.NoError(t, metrics.Shutdown(context.Background())) - logs, err := factory.CreateLogs(context.Background(), NewNopSettings(NopType), cfg, consumertest.NewNop()) + logs, err := factory.CreateLogs(context.Background(), NewNopSettings(factory.Type()), cfg, consumertest.NewNop()) require.NoError(t, err) assert.Equal(t, consumer.Capabilities{MutatesData: false}, logs.Capabilities()) assert.NoError(t, logs.Start(context.Background(), componenttest.NewNopHost())) diff --git a/processor/xprocessor/processor.go b/processor/xprocessor/processor.go index 0b0dff3e98c..92262e53a13 100644 --- a/processor/xprocessor/processor.go +++ b/processor/xprocessor/processor.go @@ -10,6 +10,7 @@ import ( "go.opentelemetry.io/collector/consumer/xconsumer" "go.opentelemetry.io/collector/pipeline" "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/internal" ) // Factory is a component.Factory interface for processors. @@ -65,6 +66,9 @@ func (f factory) CreateProfiles(ctx context.Context, set processor.Settings, cfg if f.createProfilesFunc == nil { return nil, pipeline.ErrSignalNotSupported } + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } return f.createProfilesFunc(ctx, set, cfg, next) } diff --git a/processor/xprocessor/processor_test.go b/processor/xprocessor/processor_test.go index 79c42578336..ee95f9f9275 100644 --- a/processor/xprocessor/processor_test.go +++ b/processor/xprocessor/processor_test.go @@ -8,13 +8,17 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/consumer/xconsumer" "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/internal" ) +var testID = component.MustNewID("test") + func TestNewFactoryWithProfiles(t *testing.T) { testType := component.MustNewType("test") defaultCfg := struct{}{} @@ -27,8 +31,13 @@ func TestNewFactoryWithProfiles(t *testing.T) { assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) assert.Equal(t, component.StabilityLevelAlpha, factory.ProfilesStability()) - _, err := factory.CreateProfiles(context.Background(), processor.Settings{}, &defaultCfg, consumertest.NewNop()) - assert.NoError(t, err) + _, err := factory.CreateProfiles(context.Background(), processor.Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) + require.NoError(t, err) + + wrongID := component.MustNewID("wrong") + wrongIDErrStr := internal.ErrIDMismatch(wrongID, testType).Error() + _, err = factory.CreateProfiles(context.Background(), processor.Settings{ID: wrongID}, &defaultCfg, consumertest.NewNop()) + assert.EqualError(t, err, wrongIDErrStr) } var nopInstance = &nopProcessor{ diff --git a/receiver/internal/err.go b/receiver/internal/err.go new file mode 100644 index 00000000000..7969e79afcc --- /dev/null +++ b/receiver/internal/err.go @@ -0,0 +1,14 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "go.opentelemetry.io/collector/receiver/internal" + +import ( + "fmt" + + "go.opentelemetry.io/collector/component" +) + +func ErrIDMismatch(id component.ID, typ component.Type) error { + return fmt.Errorf("component type mismatch: component ID %q does not have type %q", id, typ) +} diff --git a/receiver/otlpreceiver/go.mod b/receiver/otlpreceiver/go.mod index 7e6d3868111..f5434f5f7e5 100644 --- a/receiver/otlpreceiver/go.mod +++ b/receiver/otlpreceiver/go.mod @@ -64,7 +64,7 @@ require ( go.opentelemetry.io/collector/client v1.26.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.26.0 // indirect go.opentelemetry.io/collector/extension v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/collector/pipeline v0.120.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect @@ -104,7 +104,7 @@ replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap replace go.opentelemetry.io/collector/extension => ../../extension -replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth replace go.opentelemetry.io/collector/pdata => ../../pdata @@ -141,4 +141,4 @@ retract ( v0.69.0 // Release failed, use v0.69.1 ) -replace go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest diff --git a/receiver/otlpreceiver/otlp_test.go b/receiver/otlpreceiver/otlp_test.go index 9a58c8d5405..b3426f83769 100644 --- a/receiver/otlpreceiver/otlp_test.go +++ b/receiver/otlpreceiver/otlp_test.go @@ -47,6 +47,7 @@ import ( "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" "go.opentelemetry.io/collector/pdata/testdata" + "go.opentelemetry.io/collector/receiver/otlpreceiver/internal/metadata" "go.opentelemetry.io/collector/receiver/receivertest" ) @@ -703,7 +704,7 @@ func TestGRPCInvalidTLSCredentials(t *testing.T) { r, err := NewFactory().CreateTraces( context.Background(), - receivertest.NewNopSettings(receivertest.NopType), + receivertest.NewNopSettings(metadata.Type), cfg, consumertest.NewNop()) require.NoError(t, err) @@ -772,7 +773,7 @@ func TestHTTPInvalidTLSCredentials(t *testing.T) { // TLS is resolved during Start for HTTP. r, err := NewFactory().CreateTraces( context.Background(), - receivertest.NewNopSettings(receivertest.NopType), + receivertest.NewNopSettings(metadata.Type), cfg, consumertest.NewNop()) require.NoError(t, err) @@ -838,7 +839,7 @@ func newHTTPReceiver(t *testing.T, settings component.TelemetrySettings, endpoin } func newReceiver(t *testing.T, settings component.TelemetrySettings, cfg *Config, id component.ID, c consumertest.Consumer) component.Component { - set := receivertest.NewNopSettings(receivertest.NopType) + set := receivertest.NewNopSettings(metadata.Type) set.TelemetrySettings = settings set.ID = id r, err := newOtlpReceiver(cfg, &set) @@ -1015,7 +1016,7 @@ func TestShutdown(t *testing.T) { cfg := factory.CreateDefaultConfig().(*Config) cfg.GRPC.NetAddr.Endpoint = endpointGrpc cfg.HTTP.Endpoint = endpointHTTP - set := receivertest.NewNopSettings(receivertest.NopType) + set := receivertest.NewNopSettings(metadata.Type) set.ID = otlpReceiverID r, err := NewFactory().CreateTraces( context.Background(), diff --git a/receiver/receiver.go b/receiver/receiver.go index b102b5070c6..a90a4917430 100644 --- a/receiver/receiver.go +++ b/receiver/receiver.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/pipeline" + "go.opentelemetry.io/collector/receiver/internal" ) // Traces receiver receives traces. @@ -141,6 +142,11 @@ func (f *factory) CreateTraces(ctx context.Context, set Settings, cfg component. if f.createTracesFunc == nil { return nil, pipeline.ErrSignalNotSupported } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createTracesFunc(ctx, set, cfg, next) } @@ -148,6 +154,11 @@ func (f *factory) CreateMetrics(ctx context.Context, set Settings, cfg component if f.createMetricsFunc == nil { return nil, pipeline.ErrSignalNotSupported } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createMetricsFunc(ctx, set, cfg, next) } @@ -155,6 +166,11 @@ func (f *factory) CreateLogs(ctx context.Context, set Settings, cfg component.Co if f.createLogsFunc == nil { return nil, pipeline.ErrSignalNotSupported } + + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } + return f.createLogsFunc(ctx, set, cfg, next) } diff --git a/receiver/receiver_test.go b/receiver/receiver_test.go index 84e6c44822b..291ff3e8169 100644 --- a/receiver/receiver_test.go +++ b/receiver/receiver_test.go @@ -14,26 +14,30 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/pipeline" + "go.opentelemetry.io/collector/receiver/internal" +) + +var ( + testType = component.MustNewType("test") + testID = component.NewID(testType) ) func TestNewFactory(t *testing.T) { - testType := component.MustNewType("test") defaultCfg := struct{}{} f := NewFactory( testType, func() component.Config { return &defaultCfg }) assert.EqualValues(t, testType, f.Type()) assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig()) - _, err := f.CreateTraces(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err := f.CreateTraces(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) - _, err = f.CreateMetrics(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = f.CreateMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) - _, err = f.CreateLogs(context.Background(), Settings{}, &defaultCfg, consumertest.NewNop()) + _, err = f.CreateLogs(context.Background(), Settings{ID: testID}, &defaultCfg, consumertest.NewNop()) require.ErrorIs(t, err, pipeline.ErrSignalNotSupported) } func TestNewFactoryWithOptions(t *testing.T) { - testType := component.MustNewType("test") defaultCfg := struct{}{} f := NewFactory( testType, @@ -44,17 +48,26 @@ func TestNewFactoryWithOptions(t *testing.T) { assert.EqualValues(t, testType, f.Type()) assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig()) + wrongID := component.MustNewID("wrong") + wrongIDErrStr := internal.ErrIDMismatch(wrongID, testType).Error() + assert.Equal(t, component.StabilityLevelDeprecated, f.TracesStability()) - _, err := f.CreateTraces(context.Background(), Settings{}, &defaultCfg, nil) + _, err := f.CreateTraces(context.Background(), Settings{ID: testID}, &defaultCfg, nil) require.NoError(t, err) + _, err = f.CreateTraces(context.Background(), Settings{ID: wrongID}, &defaultCfg, nil) + require.EqualError(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelAlpha, f.MetricsStability()) - _, err = f.CreateMetrics(context.Background(), Settings{}, &defaultCfg, nil) + _, err = f.CreateMetrics(context.Background(), Settings{ID: testID}, &defaultCfg, nil) require.NoError(t, err) + _, err = f.CreateMetrics(context.Background(), Settings{ID: wrongID}, &defaultCfg, nil) + require.EqualError(t, err, wrongIDErrStr) assert.Equal(t, component.StabilityLevelStable, f.LogsStability()) - _, err = f.CreateLogs(context.Background(), Settings{}, &defaultCfg, nil) - assert.NoError(t, err) + _, err = f.CreateLogs(context.Background(), Settings{ID: testID}, &defaultCfg, nil) + require.NoError(t, err) + _, err = f.CreateLogs(context.Background(), Settings{ID: wrongID}, &defaultCfg, nil) + require.EqualError(t, err, wrongIDErrStr) } var nopInstance = &nopReceiver{ diff --git a/receiver/receivertest/contract_checker.go b/receiver/receivertest/contract_checker.go index e356093243c..5e08b6a701e 100644 --- a/receiver/receivertest/contract_checker.go +++ b/receiver/receivertest/contract_checker.go @@ -113,11 +113,11 @@ func checkConsumeContractScenario(params CheckConsumeContractParams, decisionFun var err error switch params.Signal { case pipeline.SignalLogs: - receiver, err = params.Factory.CreateLogs(ctx, NewNopSettings(NopType), params.Config, consumer) + receiver, err = params.Factory.CreateLogs(ctx, NewNopSettings(params.Factory.Type()), params.Config, consumer) case pipeline.SignalTraces: - receiver, err = params.Factory.CreateTraces(ctx, NewNopSettings(NopType), params.Config, consumer) + receiver, err = params.Factory.CreateTraces(ctx, NewNopSettings(params.Factory.Type()), params.Config, consumer) case pipeline.SignalMetrics: - receiver, err = params.Factory.CreateMetrics(ctx, NewNopSettings(NopType), params.Config, consumer) + receiver, err = params.Factory.CreateMetrics(ctx, NewNopSettings(params.Factory.Type()), params.Config, consumer) default: require.FailNow(params.T, "must specify a valid DataType to test for") } diff --git a/receiver/xreceiver/profiles.go b/receiver/xreceiver/profiles.go index 6aa5b3d0614..cc068175d90 100644 --- a/receiver/xreceiver/profiles.go +++ b/receiver/xreceiver/profiles.go @@ -10,6 +10,7 @@ import ( "go.opentelemetry.io/collector/consumer/xconsumer" "go.opentelemetry.io/collector/pipeline" "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/internal" ) // Profiles receiver receives profiles. @@ -67,6 +68,9 @@ func (f *factory) CreateProfiles(ctx context.Context, set receiver.Settings, cfg if f.createProfilesFunc == nil { return nil, pipeline.ErrSignalNotSupported } + if set.ID.Type() != f.Type() { + return nil, internal.ErrIDMismatch(set.ID, f.Type()) + } return f.createProfilesFunc(ctx, set, cfg, next) } diff --git a/receiver/xreceiver/receiver_test.go b/receiver/xreceiver/receiver_test.go index 06ff1f592f9..e649f257190 100644 --- a/receiver/xreceiver/receiver_test.go +++ b/receiver/xreceiver/receiver_test.go @@ -8,13 +8,17 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/consumer/xconsumer" "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/internal" ) +var testID = component.MustNewID("test") + func TestNewFactoryWithProfiles(t *testing.T) { testType := component.MustNewType("test") defaultCfg := struct{}{} @@ -27,8 +31,12 @@ func TestNewFactoryWithProfiles(t *testing.T) { assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) assert.Equal(t, component.StabilityLevelAlpha, factory.ProfilesStability()) - _, err := factory.CreateProfiles(context.Background(), receiver.Settings{}, &defaultCfg, nil) - assert.NoError(t, err) + _, err := factory.CreateProfiles(context.Background(), receiver.Settings{ID: testID}, &defaultCfg, nil) + require.NoError(t, err) + wrongID := component.MustNewID("wrong") + wrongIDErrStr := internal.ErrIDMismatch(wrongID, testType).Error() + _, err = factory.CreateProfiles(context.Background(), receiver.Settings{ID: wrongID}, &defaultCfg, nil) + assert.EqualError(t, err, wrongIDErrStr) } var nopInstance = &nopReceiver{ diff --git a/service/config.go b/service/config.go index 3104faaebd4..922dde3a7f8 100644 --- a/service/config.go +++ b/service/config.go @@ -15,7 +15,7 @@ type Config struct { Telemetry telemetry.Config `mapstructure:"telemetry"` // Extensions are the ordered list of extensions configured for the service. - Extensions extensions.Config `mapstructure:"extensions"` + Extensions extensions.Config `mapstructure:"extensions,omitempty"` // Pipelines are the set of data pipelines configured for the service. Pipelines pipelines.Config `mapstructure:"pipelines"` diff --git a/service/config_test.go b/service/config_test.go index 0032ac5d7eb..c72ab78f9c9 100644 --- a/service/config_test.go +++ b/service/config_test.go @@ -6,13 +6,16 @@ package service import ( "errors" "testing" + "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" config "go.opentelemetry.io/contrib/config/v0.3.0" "go.uber.org/zap/zapcore" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/confmap/xconfmap" "go.opentelemetry.io/collector/pipeline" "go.opentelemetry.io/collector/service/extensions" @@ -88,6 +91,54 @@ func TestConfigValidate(t *testing.T) { } } +func TestConfmapMarshalConfig(t *testing.T) { + telFactory := telemetry.NewFactory() + defaultTelConfig := *telFactory.CreateDefaultConfig().(*telemetry.Config) + conf := confmap.New() + + require.NoError(t, conf.Marshal(Config{ + Telemetry: defaultTelConfig, + })) + assert.Equal(t, map[string]any{ + "pipelines": map[string]any{}, + "telemetry": map[string]any{ + "logs": map[string]any{ + "encoding": "console", + "level": "info", + "error_output_paths": []any{"stderr"}, + "output_paths": []any{"stderr"}, + "sampling": map[string]any{ + "enabled": true, + "initial": 10, + "thereafter": 100, + "tick": 10 * time.Second, + }, + }, + "metrics": map[string]any{ + "level": "Normal", + "readers": []any{ + map[string]any{ + "pull": map[string]any{ + "exporter": map[string]any{ + "prometheus": map[string]any{ + "host": "localhost", + "port": 8888, + "with_resource_constant_labels": map[string]any{ + "included": []any(nil), + }, + "without_scope_info": true, + "without_type_suffix": true, + "without_units": true, + }, + }, + }, + }, + }, + }, + }, + }, conf.ToStringMap()) +} + func generateConfig() *Config { return &Config{ Telemetry: telemetry.Config{ diff --git a/service/go.mod b/service/go.mod index 7d13af5721d..1b9084b2a74 100644 --- a/service/go.mod +++ b/service/go.mod @@ -45,7 +45,7 @@ require ( go.opentelemetry.io/collector/receiver/receivertest v0.120.0 go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 go.opentelemetry.io/collector/semconv v0.120.0 - go.opentelemetry.io/collector/service/hostcapabilities v0.120.0 + go.opentelemetry.io/collector/service/hostcapabilities v0.0.0-20250225223953-66e901c716a0 go.opentelemetry.io/contrib/bridges/otelzap v0.9.0 go.opentelemetry.io/contrib/config v0.14.0 go.opentelemetry.io/contrib/propagators/b3 v1.34.0 @@ -107,7 +107,7 @@ require ( go.opentelemetry.io/collector/config/configopaque v1.26.0 // indirect go.opentelemetry.io/collector/config/configtls v1.26.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.120.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.120.0 // indirect + go.opentelemetry.io/collector/extension/extensionauth v0.0.0-20250227134758-6a98ee8b14a2 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect go.opentelemetry.io/contrib/zpages v0.59.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.10.0 // indirect @@ -182,7 +182,7 @@ replace go.opentelemetry.io/collector/featuregate => ../featuregate replace go.opentelemetry.io/collector/config/configretry => ../config/configretry -replace go.opentelemetry.io/collector/extension/auth => ../extension/auth +replace go.opentelemetry.io/collector/extension/extensionauth => ../extension/extensionauth replace go.opentelemetry.io/collector/extension/extensioncapabilities => ../extension/extensioncapabilities @@ -224,7 +224,7 @@ replace go.opentelemetry.io/collector/internal/fanoutconsumer => ../internal/fan replace go.opentelemetry.io/collector/extension/extensiontest => ../extension/extensiontest -replace go.opentelemetry.io/collector/extension/auth/authtest => ../extension/auth/authtest +replace go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../extension/extensionauth/extensionauthtest replace go.opentelemetry.io/collector/extension/xextension => ../extension/xextension diff --git a/service/hostcapabilities/go.mod b/service/hostcapabilities/go.mod index 5d370b22421..2b3fad2e915 100644 --- a/service/hostcapabilities/go.mod +++ b/service/hostcapabilities/go.mod @@ -50,8 +50,8 @@ replace ( go.opentelemetry.io/collector/exporter/exportertest => ../../exporter/exportertest go.opentelemetry.io/collector/exporter/xexporter => ../../exporter/xexporter go.opentelemetry.io/collector/extension => ../../extension - go.opentelemetry.io/collector/extension/auth => ../../extension/auth - go.opentelemetry.io/collector/extension/auth/authtest => ../../extension/auth/authtest + go.opentelemetry.io/collector/extension/extensionauth => ../../extension/extensionauth + go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest => ../../extension/extensionauth/extensionauthtest go.opentelemetry.io/collector/extension/extensioncapabilities => ../../extension/extensioncapabilities go.opentelemetry.io/collector/extension/extensiontest => ../../extension/extensiontest go.opentelemetry.io/collector/extension/xextension => ../../extension/xextension diff --git a/service/telemetry/config.go b/service/telemetry/config.go index 73f7cfb368c..41bbf5426a0 100644 --- a/service/telemetry/config.go +++ b/service/telemetry/config.go @@ -30,13 +30,13 @@ var disableAddressFieldForInternalTelemetryFeatureGate = featuregate.GlobalRegis type Config struct { Logs LogsConfig `mapstructure:"logs"` Metrics MetricsConfig `mapstructure:"metrics"` - Traces TracesConfig `mapstructure:"traces"` + Traces TracesConfig `mapstructure:"traces,omitempty"` // Resource specifies user-defined attributes to include with all emitted telemetry. // Note that some attributes are added automatically (e.g. service.version) even // if they are not specified here. In order to suppress such attributes the // attribute must be specified in this map with null YAML value (nil string pointer). - Resource map[string]*string `mapstructure:"resource"` + Resource map[string]*string `mapstructure:"resource,omitempty"` } // LogsConfig defines the configurable settings for service telemetry logs. diff --git a/service/telemetry/internal/migration/v0.3.0.go b/service/telemetry/internal/migration/v0.3.0.go index bf71ea91843..8444de56dfe 100644 --- a/service/telemetry/internal/migration/v0.3.0.go +++ b/service/telemetry/internal/migration/v0.3.0.go @@ -68,7 +68,7 @@ type MetricsConfigV030 struct { Level configtelemetry.Level `mapstructure:"level"` // Deprecated: [v0.111.0] use readers configuration. - Address string `mapstructure:"address"` + Address string `mapstructure:"address,omitempty"` // Readers allow configuration of metric readers to emit metrics to // any number of supported backends. @@ -110,7 +110,7 @@ type LogsConfigV030 struct { // Development puts the logger in development mode, which changes the // behavior of DPanicLevel and takes stacktraces more liberally. // (default = false) - Development bool `mapstructure:"development"` + Development bool `mapstructure:"development,omitempty"` // Encoding sets the logger's encoding. // Example values are "json", "console". @@ -119,13 +119,13 @@ type LogsConfigV030 struct { // DisableCaller stops annotating logs with the calling function's file // name and line number. By default, all logs are annotated. // (default = false) - DisableCaller bool `mapstructure:"disable_caller"` + DisableCaller bool `mapstructure:"disable_caller,omitempty"` // DisableStacktrace completely disables automatic stacktrace capturing. By // default, stacktraces are captured for WarnLevel and above logs in // development and ErrorLevel and above in production. // (default = false) - DisableStacktrace bool `mapstructure:"disable_stacktrace"` + DisableStacktrace bool `mapstructure:"disable_stacktrace,omitempty"` // Sampling sets a sampling policy. // Default: @@ -164,11 +164,11 @@ type LogsConfigV030 struct { // foo: "bar" // // By default, there is no initial field. - InitialFields map[string]any `mapstructure:"initial_fields"` + InitialFields map[string]any `mapstructure:"initial_fields,omitempty"` // Processors allow configuration of log record processors to emit logs to - // any number of suported backends. - Processors []config.LogRecordProcessor `mapstructure:"processors"` + // any number of supported backends. + Processors []config.LogRecordProcessor `mapstructure:"processors,omitempty"` } // LogsSamplingConfig sets a sampling strategy for the logger. Sampling caps the diff --git a/versions.yaml b/versions.yaml index 0acf477d666..882c922f04d 100644 --- a/versions.yaml +++ b/versions.yaml @@ -21,6 +21,7 @@ module-sets: - go.opentelemetry.io/collector/config/configtls - go.opentelemetry.io/collector/config/confignet - go.opentelemetry.io/collector/consumer + - go.opentelemetry.io/collector/extension beta: version: v0.120.0 modules: @@ -55,7 +56,8 @@ module-sets: - go.opentelemetry.io/collector/exporter/otlpexporter - go.opentelemetry.io/collector/exporter/otlphttpexporter - go.opentelemetry.io/collector/exporter/xexporter - - go.opentelemetry.io/collector/extension + - go.opentelemetry.io/collector/extension/extensionauth + - go.opentelemetry.io/collector/extension/extensionauth/extensionauthtest - go.opentelemetry.io/collector/extension/auth - go.opentelemetry.io/collector/extension/auth/authtest - go.opentelemetry.io/collector/extension/extensioncapabilities