diff --git a/.chloggen-aws/TEMPLATE.yaml b/.chloggen-aws/TEMPLATE.yaml new file mode 100644 index 000000000000..3b370bb1f340 --- /dev/null +++ b/.chloggen-aws/TEMPLATE.yaml @@ -0,0 +1,23 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [] + +# (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: + +# e.g. '[aws]' +# Include 'aws' if the change is done done by cwa +# Default: '[user]' +change_logs: [aws] \ No newline at end of file diff --git a/.chloggen-aws/changelog-aws.yaml b/.chloggen-aws/changelog-aws.yaml new file mode 100755 index 000000000000..83fc45a14940 --- /dev/null +++ b/.chloggen-aws/changelog-aws.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: 'new_component' + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: changelog + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Added a chloggen for cwa" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [125] + +# (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: Added a new changelog directory that stores the changelog yaml files created by the cwa team with (make chlog-new-aws) + Added a new CHANGELOG-AWS.md file for cwa team. + make chlog-update-aws will update the CHANGELOG-AWS.md file with the .chloggen-aws files and delete the .chloggen-aws directory yaml after updating. + + +# e.g. '[aws]' +# Include 'aws' if the change is done done by cwa +# Default: '[user]' +change_logs: [aws] \ No newline at end of file diff --git a/.chloggen-aws/config.yaml b/.chloggen-aws/config.yaml new file mode 100644 index 000000000000..4df7ee351e28 --- /dev/null +++ b/.chloggen-aws/config.yaml @@ -0,0 +1,23 @@ +# The directory that stores individual changelog entries. +# Each entry is stored in a dedicated yaml file. +# - 'chloggen new' will copy the 'template_yaml' to this directory as a new entry file. +# - 'chloggen validate' will validate that all entry files are valid. +# - 'chloggen update' will read and delete all entry files in this directory, and update 'changelog_md'. +# Specify as relative path from root of repo. +# (Optional) Default: .chloggen +entries_dir: .chloggen-aws + +# This file is used as the input for individual changelog entries. +# Specify as relative path from root of repo. +# (Optional) Default: .chloggen/TEMPLATE.yaml +template_yaml: .chloggen-aws/TEMPLATE.yaml + +# The CHANGELOG file or files to which 'chloggen update' will write new entries +# (Optional) Default filename: CHANGELOG.md +change_logs: + aws: CHANGELOG-AWS.md + +# The default change_log or change_logs to which an entry should be added. +# If 'change_logs' is specified in this file, and no value is specified for 'default_change_logs', +# then 'change_logs' MUST be specified in every entry file. +default_change_logs: [aws] diff --git a/.chloggen/strip-aws-sdk-local-root.yaml b/.chloggen/strip-aws-sdk-local-root.yaml new file mode 100644 index 000000000000..4c43b5405339 --- /dev/null +++ b/.chloggen/strip-aws-sdk-local-root.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: awsxrayexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Extend the AWSK.SDK prefix stripping to cover a previously missed case, for local root spans + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [27232] + +# (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: Follow-up from https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/27232 + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c2d1b3825ec6..63c8d5a5888b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -89,6 +89,7 @@ exporter/tencentcloudlogserviceexporter/ @open-telemetry/collect exporter/zipkinexporter/ @open-telemetry/collector-contrib-approvers @MovieStoreGuy @astencel-sumo @crobert-1 extension/asapauthextension/ @open-telemetry/collector-contrib-approvers @jamesmoessis @MovieStoreGuy +extension/awsmiddleware/ @open-telemetry/collector-contrib-approvers @jefchien extension/awsproxy/ @open-telemetry/collector-contrib-approvers @Aneurysm9 @mxiamxia extension/basicauthextension/ @open-telemetry/collector-contrib-approvers @jpkrohling @svrakitin @frzifus extension/bearertokenauthextension/ @open-telemetry/collector-contrib-approvers @jpkrohling @pavankrish123 @frzifus diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e1d12d3f3182..e8c495c24114 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -332,6 +332,11 @@ updates: schedule: interval: "weekly" day: "wednesday" + - package-ecosystem: "gomod" + directory: "/extension/awsmiddleware" + schedule: + interval: "weekly" + day: "wednesday" - package-ecosystem: "gomod" directory: "/extension/awsproxy" schedule: @@ -1097,8 +1102,3 @@ updates: schedule: interval: "weekly" day: "wednesday" - - package-ecosystem: "gomod" - directory: "/receiver/snmpreceiver" - schedule: - interval: "weekly" - day: "wednesday" diff --git a/.github/workflows/changelog-update.yml b/.github/workflows/changelog-update.yml new file mode 100644 index 000000000000..4c954ec340d0 --- /dev/null +++ b/.github/workflows/changelog-update.yml @@ -0,0 +1,48 @@ +name: Update changelog + +on: + pull_request: + types: + - closed +jobs: + update: + runs-on: ubuntu-latest + + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the + # updated CHANGELOG-AWS.md back to the repository. + # https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/ + contents: write + steps: + - name: Checkout action + uses: actions/checkout@v3 + - name : checkout + run : | + git config --global user.name 'Github Action' + git config --global user.email 'action@github.com' + - name: Set up Go 1.x + uses: actions/setup-go@v4 + with: + go-version: ~1.21.1 + cache: false + - name: make update changelog + run: make chlog-update-aws + - name: switching from HTTPS to SSH + run: git remote set-url origin https://github.com/amazon-contributing/opentelemetry-collector-contrib.git + - name: check for changes + run: | + git branch + git status + - name: stage changed files + run: git add ./CHANGELOG-AWS.md + - name: commit changed files + run: | + git config --global user.name 'Github Action' + git config --global user.email 'action@github.com' + git commit -m "Auto updating changelog-aws.md" + - name: pull from master + run: | + git remote -v + git pull + - name: push code to aws-cwa-dev + run: git push origin aws-cwa-dev diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 000000000000..8e6b507beb09 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,93 @@ +# This action requires that any PR targeting the aws-cwa-dev branch should add a +# yaml file to the ./.chloggen/ directory. If a CHANGELOG entry is not required, +# or if performing maintance on the Changelog, add either \"[chore]\" to the title of +# the pull request or add the \"Skip Changelog\" label to disable this action. + +name: changelog + +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] + branches: + - aws-cwa-dev +env: + # Make sure to exit early if cache segment download times out after 2 minutes. + # We limit cache download as a whole to 5 minutes. + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 2 + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + changelog: + runs-on: ubuntu-latest + if: ${{ github.actor != 'dependabot[bot]' }} + env: + PR_HEAD: ${{ github.event.pull_request.head.sha }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v4 + with: + go-version: ~1.20.10 + cache: false + - name: Cache Go + id: go-cache + timeout-minutes: 5 + uses: actions/cache@v3 + with: + path: | + ~/go/bin + ~/go/pkg/mod + key: changelog-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + - name: Ensure no changes to the CHANGELOG-AWS.md + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') && !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') && !contains(github.event.pull_request.title, '[chore]')}} + run: | + if [[ $(git diff --name-only $(git merge-base origin/main $PR_HEAD) $PR_HEAD ./CHANGELOG-AWS.md) ]] + then + echo "CHANGELOG-AWS.md should not be directly modified." + echo "Please add a .yaml file to the ./.chloggen-aws/ directory." + echo "See CONTRIBUTING-AWS.md for more details." + echo "Alternately, add either \"[chore]\" to the title of the pull request or add the \"Skip Changelog\" label if this job should be skipped." + false + else + echo "CHANGELOG-AWS.md were not modified." + fi + - name: Ensure ./.chloggen-aws/*.yaml addition(s) + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') && !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') && !contains(github.event.pull_request.title, '[chore]')}} + run: | + if [[ 1 -gt $(git diff --diff-filter=A --name-only $(git merge-base origin/main $PR_HEAD) $PR_HEAD ./.chloggen-aws | grep -c \\.yaml) ]] + then + echo "No changelog entry was added to the ./.chloggen-aws/ directory." + echo "Please add a .yaml file to the ./.chloggen-aws/ directory." + echo "See CONTRIBUTING-AWS.md for more details." + echo "Alternately, add either \"[chore]\" to the title of the pull request or add the \"Skip Changelog\" label if this job should be skipped." + false + else + ls ./.chloggen-aws + echo "A changelog entry was added to the ./.chloggen-aws/ directory." + fi + + - name: Validate ./.chloggen/*.yaml changes + run: | + make chlog-validate-aws \ + || { echo "New ./.chloggen-aws/*.yaml file failed validation."; exit 1; } + +# In order to validate any links in the yaml file, render the config to markdown + - name: Render .chloggen changelog entries + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') && !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') && !contains(github.event.pull_request.title, '[chore]')}} + run: make chlog-preview-aws > changelog_preview.md + - name: Install markdown-link-check + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') && !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') && !contains(github.event.pull_request.title, '[chore]')}} + run: npm install -g markdown-link-check + - name: Run markdown-link-check + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') && !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') && !contains(github.event.pull_request.title, '[chore]')}} + run: | + markdown-link-check \ + --verbose \ + --config .github/workflows/check_links_config.json \ + changelog_preview.md \ + || { echo "Check that anchor links are lowercase"; exit 1; } \ No newline at end of file diff --git a/CHANGELOG-AWS.md b/CHANGELOG-AWS.md new file mode 100644 index 000000000000..a6b0dce15605 --- /dev/null +++ b/CHANGELOG-AWS.md @@ -0,0 +1,10 @@ + + +# GO AWS cloudwatch agent Changelog + +This changelog includes only clodwatch agent developer-facing changes. +Please checkout contributing file [CONTRIBUTING-AWS.md](./CONTRIBUTING-AWS.md) + + + + diff --git a/CONTRIBUTING-AWS.md b/CONTRIBUTING-AWS.md new file mode 100644 index 000000000000..bc356d4bb660 --- /dev/null +++ b/CONTRIBUTING-AWS.md @@ -0,0 +1,388 @@ +# Contributing + +If you would like to contribute please read OpenTelemetry Collector [contributing +guidelines](https://github.com/open-telemetry/opentelemetry-collector/blob/main/CONTRIBUTING.md) before you begin your +work. + +## Pull-request title + +The title for your pull-request should contain the component type and name in brackets, plus a short statement for your +change. For instance: + + [processor/tailsampling] fix AND policy + +## Changelog + +### Overview + +There are two Changelogs for this repository: + +- `CHANGELOG-AWS.md` is intended for cwa team to track the changes. +### When to add a Changelog Entry + +When ever we make any changes to the contrib repo, before raising the pull request we have to create a yaml file in .changelog-aws referencing the template in the directory. + +**Examples** + +Changelog entry required: +- Changes to the configuration of the collector or any component +- Changes to the telemetry emitted from and/or processed by the collector +- Changes to the prerequisites or assumptions for running a collector +- Changes to an API exported by a collector package +- Meaningful changes to the performance of the collector + +Judgement call: +- Major changes to documentation +- Major changes to tests or test frameworks +- Changes to developer tooling in the repo + +No changelog entry: +- Typical documentation updates +- Refactorings with no meaningful change in functionality +- Most changes to tests +- Chores, such as enabling linters, or minor changes to the CI process + +### Adding a Changelog Entry + +The [CHANGELOG-AWS.md](./CHANGELOG-AWS.md) files in this repo is autogenerated from `.yaml` files in the `./.chloggen-aws` directory. + +The name of your file must be unique since the last release. + +Once the PR has been approved and submitted the changelog-update.yml gets auto trigger to update the changelog-aws.md file. +**Recommended Steps** +1. Create an entry file using `make chlog-new-aws`. This generates a file based on your current branch (e.g. `./.chloggen-aws/my-branch.yaml`) +2. Fill in all fields in the new file +3. Run `make chlog-validate-aws` to ensure the new file is valid +4. Commit and push the file + +Alternately, copy `./.chloggen-aws/TEMPLATE.yaml`, or just create your file from scratch. + +## Portable Code + +In order to ensure compatibility with different operating systems, code should be portable. Below are some guidelines to follow when writing portable code: + +* Avoid using platform-specific libraries, features etc. Please opt for portable multi-platform solutions. + +* Avoid hard-coding platform-specific values. Use environment variables or configuration files for storing platform-specific values. + + For example, avoid using hard-coded file path + ``` + filePath := "C:\Users\Bob\Documents\sampleData.csv" + ``` + + Instead environment variable or configuration file can be used. + ``` + filePath := os.Getenv("DATA_FILE_PATH") + ``` + or + ``` + filePath := Configuration.Get("data_file_path") + ``` + +* Be mindful of + - Standard file systems and file paths such as forward slashes (/) instead of backward slashes (\\) in Windows. Use the [`path/filepath` package](https://pkg.go.dev/path/filepath) when working with filepaths. + - Consistent line ending formats such as Unix (LF) or Windows (CRLF). + +* Test your implementation thoroughly on different platforms if possible and fix any issues. + +With above guidelines, you can write code that is more portable and easier to maintain across different platforms. + +## Adding New Components + +**Before** any code is written, [open an +issue](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/new?assignees=&labels=new+component&template=new_component.md&title=New%20component) +providing the following information: + +* Who's the sponsor for your component. A sponsor is an approver who will be in charge of being the official reviewer of + the code and become a code owner for the component. For vendor-specific components, it's good to have a volunteer + sponsor. If you can't find one, we'll assign one in a round-robin fashion. A vendor-specific component directly interfaces + with a vendor-specific API and is expected to be maintained by a representative of the same vendor. For non-vendor specific + components, having a sponsor means that your use case has been validated. +* Some information about your component, such as the reasoning behind it, use-cases, telemetry data types supported, and + anything else you think is relevant for us to make a decision about accepting the component. +* The configuration options your component will accept. This will help us understand what it does and have an idea of + how the implementation might look like. + +Components refer to connectors, exporters, extensions, processors, and receivers. The key criteria to implementing a component is to: + +* Implement the [component.Component](https://pkg.go.dev/go.opentelemetry.io/collector/component#Component) interface +* Provide a configuration structure which defines the configuration of the component +* Provide the implementation which performs the component operation +* Have a `metadata.yaml` file and its generated code (using [mdatadgen](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/cmd/mdatagen/README.md)). + +Familiarize yourself with the interface of the component that you want to write, and use existing implementations as reference. +[Building a Trace Receiver](https://opentelemetry.io/docs/collector/trace-receiver/) tutorial provides a detailed example of building a component. + +*NOTICE:* The Collector is in Beta stage and as such the interfaces may undergo breaking changes. Component creators +must be available to update or review their components when such changes happen, otherwise the component will be +excluded from the default builds. + +Generally, maintenance of components is the responsibility of contributors who authored them. If the original author or +some other contributor does not maintain the component it may be excluded from the default build. The component **will** +be excluded if it causes build problems, has failing tests or otherwise causes problems to the rest of the repository +and the rest of contributors. + +- Create your component under the proper folder and use Go standard package naming recommendations. +- Use a boiler-plate Makefile that just references the one at top level, ie.: `include ../../Makefile.Common` - this + allows you to build your component with required build configurations for the contrib repo while avoiding building the + full repo during development. +- Each component has its own go.mod file. This allows custom builds of the collector to take a limited sets of + dependencies - so run `go mod` commands as appropriate for your component. +- Implement the needed interface on your component by importing the appropriate component from the core repo. Follow the + pattern of existing components regarding config and factory source files and tests. +- Implement your component as appropriate. Provide end-to-end tests (or mock backend/client as appropriate). Target is + to get 80% or more of code coverage. +- Add a README.md on the root of your component describing its configuration and usage, likely referencing some of the + yaml files used in the component tests. We also suggest that the yaml files used in tests have comments for all + available configuration settings so users can copy and modify them as needed. +- Run `make crosslink` to update intra-repository dependencies. It will add a `replace` directive to `go.mod` file of every intra-repository dependant. This is necessary for your component to be included in the contrib executable. +- Add your component to `versions.yaml`. +- All components included in the distribution must be included in [`cmd/otelcontribcol/builder-config.yaml`](./cmd/otelcontribcol/builder-config.yaml) + and in the respective testing harnesses. To align with the test goal of the project, components must be testable within the framework defined within + the folder. If a component can not be properly tested within the existing framework, it must increase the non testable + components number with a comment within the PR explaining as to why it can not be tested. +- Create a `metadata.yaml` file with at minimum the required fields defined in [metadata-schema.yaml](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/cmd/mdatagen/metadata-schema.yaml). +Here is a minimal representation: +``` +type: + +status: + class: + stability: + development: [] + codeowners: + active: [, ] +``` +- Run `make generate-gh-issue-templates` to add your component to the dropdown list in the issue templates. +- For README.md, you can start with the following: +``` +# +<!-- status autogenerated section --> +<!-- end autogenerated section --> +``` +- Create a `doc.go` file with a generate pragma. For a `fooreceiver`, the file will look like: +``` +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +// Package fooreceiver bars. +package fooreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/fooreceiver" +``` +- Type `make update-codeowners`. This will trigger the regeneration of the `.github/CODEOWNERS` file and the [metadata generator](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/cmd/mdatagen/README.md#using-the-metadata-generator) to generate the associated code/documentation. + +When submitting a component to the community, consider breaking it down into separate PRs as follows: + +* **First PR** should include the overall structure of the new component: + * Readme, configuration, and factory implementation usually using the helper + factory structs. + * This PR is usually trivial to review, so the size limit does not apply to + it. + * The component should use [`In Development` Stability](https://github.com/open-telemetry/opentelemetry-collector#development) in its README. +* **Second PR** should include the concrete implementation of the component. If the + size of this PR is larger than the recommended size consider splitting it in + multiple PRs. +* **Last PR** should mark the new component as `Alpha` stability and add it to the `cmd/otelcontribcol` + binary by updating the `cmd/otelcontribcol/components.go` file. The component must be enabled + only after sufficient testing and only when it meets [`Alpha` stability requirements](https://github.com/open-telemetry/opentelemetry-collector#alpha). +* Once a new component has been added to the executable, please add the component + to the [OpenTelemetry.io registry](https://github.com/open-telemetry/opentelemetry.io#adding-a-project-to-the-opentelemetry-registry). + +### Releasing New Components +After a component has been approved and merged, and has been enabled in `internal/components/`, it must be added to the +[OpenTelemetry Collector Contrib's release manifest.yaml](https://github.com/open-telemetry/opentelemetry-collector-releases/blob/main/distributions/otelcol-contrib/manifest.yaml) +to be included in the distributed otelcol-contrib binaries and docker images. + +### Rotating sponsors + +The following GitHub users are the currently available sponsors, either by being an approver or a maintainer of the contrib repository. The list is ordered based on a random sort of the list of sponsors done live at the Collector SIG meeting on 27-Apr-2022 and serves as the seed for the round-robin selection of sponsors, as described in the section above. + +* [@djaglowski](https://github.com/djaglowski) +* [@codeboten](https://github.com/codeboten) +* [@Aneurysm9](https://github.com/Aneurysm9) +* [@mx-psi](https://github.com/mx-psi) +* [@dmitryax](https://github.com/dmitryax) +* [@evan-bradley](https://github.com/evan-bradley) +* [@MovieStoreGuy](https://github.com/MovieStoreGuy) +* [@bogdandrutu](https://github.com/bogdandrutu) +* [@jpkrohling](https://github.com/jpkrohling) +* [@dashpole](https://github.com/dashpole) +* [@TylerHelmuth](https://github.com/TylerHelmuth) + +Whenever a sponsor is picked from the top of this list, please move them to the bottom. + +## Adding metrics to existing receivers +Following these steps for contributing additional metrics to existing receivers. + - Read instructions [here](https://github.com/open-telemetry/opentelemetry-collector/blob/main/CONTRIBUTING.md#fork) on how to + fork, build and create PRs. The only difference is to change repository name from `opentelemetry-collector` to `opentelemetry-collector-contrib` + - Edit `metadata.yaml` of your metrics receiver to add new metrics, e.g.: `redisreceiver/metadata.yaml` + - To generate new metrics on top of this updated YAML file. + - Run `cd receiver/redisreceiver` + - Run `go generate ./...` +- Review the changed files and merge the changes into your forked repo. +- Create PR from Github web console following the instructions above. + +## General Recommendations +Below are some recommendations that apply to typical components. These are not rigid rules and there are exceptions but +in general try to follow them. + +- Avoid introducing batching, retries or worker pools directly on receivers and exporters. Typically, these are general + cases that can be better handled via processors (that also can be reused by other receivers and exporters). +- When implementing exporters try to leverage the exporter helpers from the core repo, see [exporterhelper + package](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/exporterhelper). This will + ensure that the exporter provides [zPages](https://opencensus.io/zpages/) and a standard set of metrics. +- `replace` statements in `go.mod` files can be automatically inserted by running `make crosslink`. For more information + on the `crosslink` tool see the README [here](https://github.com/open-telemetry/opentelemetry-go-build-tools/tree/main/crosslink). + +## Issue Triaging + +To help provide a consistent process for seeing issues through to completion, this section details some guidelines and +definitions to keep in mind when triaging issues. + +### Roles + +Determining the root cause of issues is a shared responsibility between those with triager permissions, code owners, +OpenTelemetry community members, issue authors, and anyone else who would like to contribute. + +#### Triagers + +Contributors with [triager](https://github.com/open-telemetry/opentelemetry-collector-contrib/#contributing) permissions can help move +issues along by adding missing component labels, which help organize issues and trigger automations to notify code owners. They can +also use their familiarity with the Collector and its components to investigate issues themselves. Alternatively, they may point issue +authors to another resource or someone else who may know more. + +#### Code Owners + +In many cases, the code owners for an issue are the best resource to help determine the root cause of a bug or whether an enhancement +is fit to be added to a component. Code owners will be notified by repository automations when: + +- a component label is added to an issue +- an issue is opened +- the issue becomes stale + +Code owners may not have triager permissions on the repository, +so they can help triage through investigation and by participating in discussions. They can also help organize issues by +[adding labels via comments](#adding-labels-via-comments). + +#### Community Members + +Community members or interested parties are welcome to help triage issues by investigating the root cause of bugs, adding input for +features they would like to see, or participating in design discussions. + +### Triage process + +Triaging an issue requires getting the issue into a state where there is enough information available on the issue or understanding +between the involved parties to allow work to begin or for the issue to be closed. Facilitating this may involve, but is not limited to: + +- Determining whether the issue is related to the code or documentation, or whether the issue can be resolved without any changes. +- Ensuring that a bug can be reproduced, and if possible, the behavior can be traced back to the offending code or documentation. +- Determining whether a feature request belongs in a component, should be accomplished through other means, or isn't appropriate for a component at this time. +- Guiding any interested parties to another person or resource that may be more knowledgeable about an issue. +- Suggesting an issue for discussion at a SIG meeting if a synchronous discussion would be more productive. + +#### Issue assignment + +Issues are assigned for someone to work on by a triager when someone volunteers to work on an issue. Assignment is intended to prevent duplicate work by making it visible who is +working on a particular task. A person who is assigned to the issue may be assigned to help triage the issue and implement it, or can be assigned after the issue has already been +triaged and is ready for work. If someone who is assigned to an issue is no longer able to work on it, they may request to be unassigned from the issue. + +### Label Definitions + +| Label | When to apply | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `bug` | Something that is advertised or intended to work isn't working as expected. | +| `enhancement` | Something that isn't an advertised feature that would be useful to users or maintainers. | +| `flaky test` | A test unexpectedly failed during CI, showing that there is a problem with the tests or test setup that is causing the tests to intermittently fail. | +| `good first issue` | Implementing this issue would not require specialized or in-depth knowledge about the component and is ideal for a new or first-time contributor to take. | +| `help wanted` | The code owners for this component do not expect to have time to work on it soon, and would welcome help from contributors. | +| `discussion needed` | This issue needs more input from the maintainers or community before work can be started. | +| `needs triage` | This label is added automatically, and can be removed when a triager or code owner deems that an issue is either ready for work or should not need any work. | +| `waiting for author` | Can be applied when input is required from the author before the issue can move any further. | +| `priority:p0` | A critical security vulnerability or Collector panic using a default or common configuration unrelated to a specific component. | +| `priority:p1` | An urgent issue that should be worked on quickly, before most other issues. | +| `priority:p2` | A standard bug or enhancement. | +| `priority:p3` | A technical improvement, lower priority bug, or other minor issue. Generally something that is considered a "nice to have." | +| `release:blocker` | This issue must be resolved before the next Collector version can be released. | +| `Sponsor Needed` | A new component has been proposed, but implementation is not ready to begin. This can be because a sponsor has not yet been decided, or because some details on the component still need to be decided. | +| `Accepted Component` | A sponsor has elected to take on a component and implementation is ready to begin. | +| `Vendor Specific Component` | This should be applied to any component proposal where the functionality for the component is particular to a vendor. | + +### Adding Labels via Comments + +In order to facilitate proper label usage and to empower Code Owners, you are able to add labels to issues via comments. To add a label through a comment, post a new comment on an issue starting with `/label`, followed by a space-separated list of your desired labels. Supported labels come from the table below, or correspond to a component defined in the [CODEOWNERS file](.github/CODEOWNERS). + +The following general labels are supported: + +| Label | Label in Comment | +|----------------------|----------------------| +| `good first issue` | `good-first-issue` | +| `help wanted` | `help-wanted` | +| `discussion needed` | `discussion-needed` | +| `needs triage` | `needs-triage` | +| `waiting for author` | `waiting-for-author` | + +To delete a label, prepend the label with `-`. Note that you must make a new comment to modify labels; you cannot edit an existing comment. + +Example label comment: + +``` +/label receiver/prometheus help-wanted -exporter/prometheus +``` + +## Becoming a Code Owner + +A Code Owner is responsible for a component within Collector Contrib, as indicated by the [CODEOWNERS file](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/.github/CODEOWNERS). That responsibility includes maintaining the component, responding to issues, and reviewing pull requests. + +Sometimes a component may be in need of a new or additional Code Owner. A few reasons this situation may arise would be: +- The component was never assigned a Code Owner. +- A previous Code Owner stepped down. +- An existing Code Owner has become unresponsive. See [unmaintained stability status](https://github.com/open-telemetry/opentelemetry-collector#unmaintained). +- The existing Code Owners are actively looking for new Code Owners to help. + +If you would like to help and become a Code Owner you must meet the following requirements: + +1. [Be a member of the OpenTelemetry organization.](https://github.com/open-telemetry/community/blob/main/community-membership.md#member) +2. (Code Owner Discretion) It is best to have resolved an issue related to the component, contributed directly to the component, and/or review component PRs. How much interaction with the component is required before becoming a Code Owner is up to any existing Code Owners. + +Code Ownership is ultimately up to the judgement of the existing Code Owners and Collector Contrib Maintainers. Meeting the above requirements is not a guarantee to be granted Code Ownership. + +To become a Code Owner, open a PR with the CODEOWNERS file modified, adding your GitHub username to the component's row. Be sure to tag the existing Code Owners, if any, within the PR to ensure they receive a notification. + +### Makefile Guidelines + +When adding or modifying the `Makefile`'s in this repository, consider the following design guidelines. + +Make targets are organized according to whether they apply to the entire repository, or only to an individual module. +The [Makefile](./Makefile) SHOULD contain "repo-level" targets. (i.e. targets that apply to the entire repo.) +Likewise, `Makefile.Common` SHOULD contain "module-level" targets. (i.e. targets that apply to one module at a time.) +Each module should have a `Makefile` at its root that includes `Makefile.Common`. + +#### Module-level targets + +Module-level targets SHOULD NOT act on nested modules. For example, running `make lint` at the root of the repo will +*only* evaluate code that is part of the `go.opentelemetry.io/collector` module. This excludes nested modules such as +`go.opentelemetry.io/collector/component`. + +Each module-level target SHOULD have a corresponding repo-level target. For example, `make golint` will run `make lint` +in each module. In this way, the entire repository is covered. The root `Makefile` contains some "for each module" targets +that can wrap a module-level target into a repo-level target. + +#### Repo-level targets + +Whenever reasonable, targets SHOULD be implemented as module-level targets (and wrapped with a repo-level target). +However, there are many valid justifications for implementing a standalone repo-level target. + +1. The target naturally applies to the repo as a whole. (e.g. Building the collector.) +2. Interaction between modules would be problematic. +3. A necessary tool does not provide a mechanism for scoping its application. (e.g. `porto` cannot be limited to a specific module.) +4. The "for each module" pattern would result in incomplete coverage of the codebase. (e.g. A target that scans all file, not just `.go` files.) + +#### Default targets + +The default module-level target (i.e. running `make` in the context of an individual module), should run a substantial set of module-level +targets for an individual module. Ideally, this would include *all* module-level targets, but exceptions should be made if a particular +target would result in unacceptable latency in the local development loop. + +The default repo-level target (i.e. running `make` at the root of the repo) should meaningfully validate the entire repo. This should include +running the default common target for each module as well as additional repo-level targets. diff --git a/Makefile b/Makefile index 9562d95f6f7f..0244e87cbeeb 100644 --- a/Makefile +++ b/Makefile @@ -288,6 +288,22 @@ chlog-preview: $(CHLOGGEN) chlog-update: $(CHLOGGEN) $(CHLOGGEN) update --config $(CHLOGGEN_CONFIG) --version $(VERSION) +.PHONY: chlog-new-aws +chlog-new-aws: $(CHLOGGEN) + $(CHLOGGEN) new --config $(CHLOGGEN_CONFIG_AWS) --filename $(FILENAME) + +.PHONY: chlog-validate-aws +chlog-validate-aws: $(CHLOGGEN) + $(CHLOGGEN) validate --config $(CHLOGGEN_CONFIG_AWS) + +.PHONY: chlog-preview-aws +chlog-preview-aws: $(CHLOGGEN) + $(CHLOGGEN) update --config $(CHLOGGEN_CONFIG_AWS) --dry + +.PHONY: chlog-update-aws +chlog-update-aws: $(CHLOGGEN) + $(CHLOGGEN) update --config $(CHLOGGEN_CONFIG_AWS) --version `date +'%y.%m.%d %H:%M:%S'` + .PHONY: genotelcontribcol genotelcontribcol: $(BUILDER) $(BUILDER) --skip-compilation --config cmd/otelcontribcol/builder-config.yaml --output-path cmd/otelcontribcol diff --git a/Makefile.Common b/Makefile.Common index 8f76bf502522..4fdf70af0b5f 100644 --- a/Makefile.Common +++ b/Makefile.Common @@ -35,6 +35,7 @@ TOOLS_PKG_NAMES := $(shell grep -E $(TOOLS_MOD_REGEX) < $(TOOLS_MOD_DIR)/tools. TOOLS_BIN_DIR := $(SRC_ROOT)/.tools TOOLS_BIN_NAMES := $(addprefix $(TOOLS_BIN_DIR)/, $(notdir $(TOOLS_PKG_NAMES))) CHLOGGEN_CONFIG := .chloggen/config.yaml +CHLOGGEN_CONFIG_AWS := .chloggen-aws/config.yaml .PHONY: install-tools install-tools: $(TOOLS_BIN_NAMES) diff --git a/cmd/checkapi/allowlist.txt b/cmd/checkapi/allowlist.txt index 72745f041595..cef9033c2b3c 100644 --- a/cmd/checkapi/allowlist.txt +++ b/cmd/checkapi/allowlist.txt @@ -13,6 +13,7 @@ exporter/lokiexporter exporter/pulsarexporter exporter/sentryexporter exporter/sumologicexporter +extension/awsmiddleware extension/observer/ecsobserver extension/observer extension/observer/k8sobserver diff --git a/cmd/configschema/go.mod b/cmd/configschema/go.mod index 5f3af880ec17..843ea305f7ae 100644 --- a/cmd/configschema/go.mod +++ b/cmd/configschema/go.mod @@ -268,6 +268,7 @@ require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/aliyun/aliyun-log-go-sdk v0.1.54 // indirect + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 // indirect github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20230818193829-04a761abd409 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/antonmedv/expr v1.15.0 // indirect @@ -277,8 +278,8 @@ require ( github.com/apache/thrift v0.19.0 // indirect github.com/ardielle/ardielle-go v1.5.2 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.45.2 // indirect - github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect + github.com/aws/aws-sdk-go v1.45.24 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.38 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.36 // indirect @@ -287,17 +288,17 @@ require ( github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect - github.com/aws/smithy-go v1.14.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect diff --git a/cmd/configschema/go.sum b/cmd/configschema/go.sum index 73bf9743a172..46bc54fe4d77 100644 --- a/cmd/configschema/go.sum +++ b/cmd/configschema/go.sum @@ -889,6 +889,8 @@ github.com/alexflint/go-filemutex v1.2.0/go.mod h1:mYyQSWvw9Tx2/H2n9qXPb52tTYfE0 github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/aliyun/aliyun-log-go-sdk v0.1.54 h1:ejQygZTGBqTs4V9qQUunWYtFwyKUWXYryfgrX9OhOlg= github.com/aliyun/aliyun-log-go-sdk v0.1.54/go.mod h1:/U0mxwX7uG2K2fbfsF92BR64zmbmJyx7WQtyKaCdRL8= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 h1:uUCPnX2C5C36iyX46N1eUjmp4LRpSTGeexgUWmohv7c= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590/go.mod h1:uOQa5/9Jle9VADEdWCXL4AbJr35NJQil30tapcTHQlw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -944,14 +946,15 @@ github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9 github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= -github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.24 h1:TZx/CizkmCQn8Rtsb11iLYutEQVGK5PK9wAhwouELBo= +github.com/aws/aws-sdk-go v1.45.24/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= @@ -979,24 +982,29 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8= github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5 h1:naSZmQiFjoTLxNjfDy/KgEnWdG3odkR6gIEgTx21YOM= github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5/go.mod h1:0h3hOcyFXyjvI3wGt8C8vk2+II9XxHwFM7zH2KvLHmA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= @@ -1010,8 +1018,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvT github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw= github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= diff --git a/cmd/otelcontribcol/go.mod b/cmd/otelcontribcol/go.mod index d41648fb0396..35c325ec7164 100644 --- a/cmd/otelcontribcol/go.mod +++ b/cmd/otelcontribcol/go.mod @@ -289,6 +289,7 @@ require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/aliyun/aliyun-log-go-sdk v0.1.54 // indirect + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 // indirect github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20230818193829-04a761abd409 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/antonmedv/expr v1.15.0 // indirect @@ -298,8 +299,8 @@ require ( github.com/apache/thrift v0.19.0 // indirect github.com/ardielle/ardielle-go v1.5.2 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.45.2 // indirect - github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect + github.com/aws/aws-sdk-go v1.45.24 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.38 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.36 // indirect @@ -308,17 +309,17 @@ require ( github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect - github.com/aws/smithy-go v1.14.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect diff --git a/cmd/otelcontribcol/go.sum b/cmd/otelcontribcol/go.sum index 549c111ffc26..ec46b06ccfcb 100644 --- a/cmd/otelcontribcol/go.sum +++ b/cmd/otelcontribcol/go.sum @@ -835,6 +835,8 @@ github.com/alexflint/go-filemutex v1.2.0/go.mod h1:mYyQSWvw9Tx2/H2n9qXPb52tTYfE0 github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/aliyun/aliyun-log-go-sdk v0.1.54 h1:ejQygZTGBqTs4V9qQUunWYtFwyKUWXYryfgrX9OhOlg= github.com/aliyun/aliyun-log-go-sdk v0.1.54/go.mod h1:/U0mxwX7uG2K2fbfsF92BR64zmbmJyx7WQtyKaCdRL8= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 h1:uUCPnX2C5C36iyX46N1eUjmp4LRpSTGeexgUWmohv7c= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590/go.mod h1:uOQa5/9Jle9VADEdWCXL4AbJr35NJQil30tapcTHQlw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -890,14 +892,15 @@ github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9 github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= -github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.24 h1:TZx/CizkmCQn8Rtsb11iLYutEQVGK5PK9wAhwouELBo= +github.com/aws/aws-sdk-go v1.45.24/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= @@ -925,24 +928,29 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8= github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5 h1:naSZmQiFjoTLxNjfDy/KgEnWdG3odkR6gIEgTx21YOM= github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5/go.mod h1:0h3hOcyFXyjvI3wGt8C8vk2+II9XxHwFM7zH2KvLHmA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= @@ -956,8 +964,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvT github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw= github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= diff --git a/exporter/awscloudwatchlogsexporter/config.go b/exporter/awscloudwatchlogsexporter/config.go index 63aaab72df58..ec5c5c877bb8 100644 --- a/exporter/awscloudwatchlogsexporter/config.go +++ b/exporter/awscloudwatchlogsexporter/config.go @@ -8,7 +8,6 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs" @@ -45,8 +44,6 @@ type Config struct { // because only QueueSize is user-settable due to how AWS CloudWatch API works QueueSettings QueueSettings `mapstructure:"sending_queue"` - logger *zap.Logger - awsutil.AWSSessionSettings `mapstructure:",squash"` // Export raw log string instead of log wrapper @@ -56,6 +53,9 @@ type Config struct { // Only allow emf logs // If this is true raw log must also be true EmfOnly bool `mapstructure:"emf_only,omitempty"` + + // MiddlewareID is an ID for an extension that can be used to configure the AWS client. + MiddlewareID *component.ID `mapstructure:"middleware,omitempty"` } type QueueSettings struct { diff --git a/exporter/awscloudwatchlogsexporter/exporter.go b/exporter/awscloudwatchlogsexporter/exporter.go index c2f7cfabe230..3777dca218cc 100644 --- a/exporter/awscloudwatchlogsexporter/exporter.go +++ b/exporter/awscloudwatchlogsexporter/exporter.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/google/uuid" @@ -52,8 +53,6 @@ func newCwLogsPusher(expConfig *Config, params exp.CreateSettings) (*exporter, e return nil, errors.New("awscloudwatchlogs exporter config is nil") } - expConfig.logger = params.Logger - // create AWS session awsConfig, session, err := awsutil.GetAWSConfigSession(params.Logger, &awsutil.Conn{}, &expConfig.AWSSessionSettings) if err != nil { @@ -63,29 +62,17 @@ func newCwLogsPusher(expConfig *Config, params exp.CreateSettings) (*exporter, e // create CWLogs client with aws session config svcStructuredLog := cwlogs.NewClient(params.Logger, awsConfig, params.BuildInfo, expConfig.LogGroupName, expConfig.LogRetention, expConfig.Tags, session) collectorIdentifier, err := uuid.NewRandom() - if err != nil { return nil, err } - pusherKey := cwlogs.PusherKey{ - LogGroupName: expConfig.LogGroupName, - LogStreamName: expConfig.LogStreamName, - } - - pusher := cwlogs.NewPusher(pusherKey, *awsConfig.MaxRetries, *svcStructuredLog, params.Logger) - - pusherMap := make(map[cwlogs.PusherKey]cwlogs.Pusher) - - pusherMap[pusherKey] = pusher - logsExporter := &exporter{ svcStructuredLog: svcStructuredLog, Config: expConfig, logger: params.Logger, retryCount: *awsConfig.MaxRetries, collectorID: collectorIdentifier.String(), - pusherMap: pusherMap, + pusherMap: make(map[cwlogs.PusherKey]cwlogs.Pusher), } return logsExporter, nil } @@ -104,6 +91,7 @@ func newCwLogsExporter(config component.Config, params exp.CreateSettings) (exp. exporterhelper.WithQueue(expConfig.enforcedQueueSettings()), exporterhelper.WithRetry(expConfig.RetrySettings), exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + exporterhelper.WithStart(logsPusher.start), exporterhelper.WithShutdown(logsPusher.shutdown), ) } @@ -120,7 +108,7 @@ func (e *exporter) consumeLogs(_ context.Context, ld plog.Logs) error { LogGroupName: logEvent.LogGroupName, LogStreamName: logEvent.LogStreamName, } - cwLogsPusher := e.getLogPusher(logEvent) + cwLogsPusher := e.getLogPusher(pusherKey) e.logger.Debug("Adding log event", zap.Any("event", logEvent)) err := cwLogsPusher.AddLogEntry(logEvent) if err != nil { @@ -146,13 +134,9 @@ func (e *exporter) consumeLogs(_ context.Context, ld plog.Logs) error { return nil } -func (e *exporter) getLogPusher(logEvent *cwlogs.Event) cwlogs.Pusher { +func (e *exporter) getLogPusher(pusherKey cwlogs.PusherKey) cwlogs.Pusher { e.pusherMapLock.Lock() defer e.pusherMapLock.Unlock() - pusherKey := cwlogs.PusherKey{ - LogGroupName: logEvent.LogGroupName, - LogStreamName: logEvent.LogStreamName, - } if e.pusherMap[pusherKey] == nil { pusher := cwlogs.NewPusher(pusherKey, e.retryCount, *e.svcStructuredLog, e.logger) e.pusherMap[pusherKey] = pusher @@ -160,6 +144,18 @@ func (e *exporter) getLogPusher(logEvent *cwlogs.Event) cwlogs.Pusher { return e.pusherMap[pusherKey] } +func (e *exporter) start(_ context.Context, host component.Host) error { + if e.Config.MiddlewareID != nil { + awsmiddleware.TryConfigure(e.logger, host, *e.Config.MiddlewareID, awsmiddleware.SDKv1(e.svcStructuredLog.Handlers())) + } + pusherKey := cwlogs.PusherKey{ + LogGroupName: e.Config.LogGroupName, + LogStreamName: e.Config.LogStreamName, + } + _ = e.getLogPusher(pusherKey) + return nil +} + func (e *exporter) shutdown(_ context.Context) error { if e.pusherMap != nil { for _, pusher := range e.pusherMap { diff --git a/exporter/awscloudwatchlogsexporter/exporter_test.go b/exporter/awscloudwatchlogsexporter/exporter_test.go index 2faab811993f..a2f9d3fbf740 100644 --- a/exporter/awscloudwatchlogsexporter/exporter_test.go +++ b/exporter/awscloudwatchlogsexporter/exporter_test.go @@ -5,15 +5,18 @@ package awscloudwatchlogsexporter import ( "context" + "os" "testing" "time" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" @@ -21,6 +24,11 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs" ) +func init() { + os.Setenv("AWS_ACCESS_KEY_ID", "test") + os.Setenv("AWS_SECRET_ACCESS_KEY", "test") +} + type mockPusher struct { mock.Mock } @@ -343,6 +351,43 @@ func TestConsumeLogs(t *testing.T) { require.NoError(t, exp.shutdown(ctx)) } +func TestMiddleware(t *testing.T) { + id := component.NewID("test") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + factory := NewFactory() + expCfg := factory.CreateDefaultConfig().(*Config) + expCfg.Region = "us-west-2" + expCfg.LogGroupName = "testGroup" + expCfg.LogStreamName = "testStream" + expCfg.MaxRetries = 0 + expCfg.MiddlewareID = &id + handler := new(awsmiddleware.MockHandler) + handler.On("ID").Return("test") + handler.On("Position").Return(awsmiddleware.After) + handler.On("HandleRequest", mock.Anything, mock.Anything) + handler.On("HandleResponse", mock.Anything, mock.Anything) + middleware := new(awsmiddleware.MockMiddlewareExtension) + middleware.On("Handlers").Return([]awsmiddleware.RequestHandler{handler}, []awsmiddleware.ResponseHandler{handler}) + extensions := map[component.ID]component.Component{id: middleware} + exp, err := newCwLogsPusher(expCfg, exportertest.NewNopCreateSettings()) + assert.Nil(t, err) + assert.NotNil(t, exp) + host := new(awsmiddleware.MockExtensionsHost) + host.On("GetExtensions").Return(extensions) + assert.NoError(t, exp.start(ctx, host)) + ld := plog.NewLogs() + r := ld.ResourceLogs().AppendEmpty() + r.Resource().Attributes().PutStr("hello", "test") + logRecords := r.ScopeLogs().AppendEmpty().LogRecords() + logRecords.EnsureCapacity(5) + logRecords.AppendEmpty() + require.Error(t, exp.consumeLogs(ctx, ld)) + require.NoError(t, exp.shutdown(ctx)) + handler.AssertCalled(t, "HandleRequest", mock.Anything, mock.Anything) + handler.AssertCalled(t, "HandleResponse", mock.Anything, mock.Anything) +} + func TestNewExporterWithoutRegionErr(t *testing.T) { factory := NewFactory() expCfg := factory.CreateDefaultConfig().(*Config) diff --git a/exporter/awscloudwatchlogsexporter/go.mod b/exporter/awscloudwatchlogsexporter/go.mod index ba21c22805bf..cbb7a97304a5 100644 --- a/exporter/awscloudwatchlogsexporter/go.mod +++ b/exporter/awscloudwatchlogsexporter/go.mod @@ -3,6 +3,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsclo go 1.20 require ( + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 github.com/aws/aws-sdk-go v1.45.2 github.com/cenkalti/backoff/v4 v4.2.1 github.com/google/uuid v1.3.1 @@ -20,6 +21,8 @@ require ( require ( github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20230818193829-04a761abd409 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -63,6 +66,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/c replace github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws => ../../override/aws +replace github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => ../../extension/awsmiddleware + retract ( v0.76.2 v0.76.1 diff --git a/exporter/awscloudwatchlogsexporter/go.sum b/exporter/awscloudwatchlogsexporter/go.sum index b6500d3bb47f..8c01c9c3be86 100644 --- a/exporter/awscloudwatchlogsexporter/go.sum +++ b/exporter/awscloudwatchlogsexporter/go.sum @@ -15,15 +15,28 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -106,6 +119,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/exporter/awsemfexporter/config.go b/exporter/awsemfexporter/config.go index a632721bec4e..f98a03136396 100644 --- a/exporter/awsemfexporter/config.go +++ b/exporter/awsemfexporter/config.go @@ -4,6 +4,8 @@ package awsemfexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsemfexporter" import ( + "strings" + "go.opentelemetry.io/collector/component" "go.uber.org/zap" @@ -95,6 +97,9 @@ type Config struct { // Otherwise, sending metrics as Embedded Metric Format version 0 (without "_aws") Version string `mapstructure:"version"` + // MiddlewareID is an ID for an extension that can be used to configure the AWS client. + MiddlewareID *component.ID `mapstructure:"middleware,omitempty"` + // logger is the Logger used for writing error/warning logs logger *zap.Logger } @@ -142,7 +147,22 @@ func (config *Config) Validate() error { } return cwlogs.ValidateTagsInput(config.Tags) +} + +func (config *Config) IsEnhancedContainerInsights() bool { + return config.EnhancedContainerInsights && !config.DisableMetricExtraction +} + +func (config *Config) IsAppSignalsEnabled() bool { + if config.LogGroupName == "" || config.Namespace == "" { + return false + } + + if config.Namespace == appSignalsMetricNamespace && strings.HasPrefix(config.LogGroupName, appSignalsLogGroupNamePrefix) { + return true + } + return false } func newEMFSupportedUnits() map[string]interface{} { diff --git a/exporter/awsemfexporter/config_test.go b/exporter/awsemfexporter/config_test.go index 9f66cc0aec4d..ddd1a24e956e 100644 --- a/exporter/awsemfexporter/config_test.go +++ b/exporter/awsemfexporter/config_test.go @@ -314,3 +314,70 @@ func TestNoDimensionRollupFeatureGate(t *testing.T) { assert.Equal(t, cfg.(*Config).DimensionRollupOption, "NoDimensionRollup") _ = featuregate.GlobalRegistry().Set("awsemf.nodimrollupdefault", false) } + +func TestIsEnhancedContainerInsights(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.EnhancedContainerInsights = true + cfg.DisableMetricExtraction = false + assert.True(t, cfg.IsEnhancedContainerInsights()) + cfg.EnhancedContainerInsights = false + assert.False(t, cfg.IsEnhancedContainerInsights()) + cfg.EnhancedContainerInsights = true + cfg.DisableMetricExtraction = true + assert.False(t, cfg.IsEnhancedContainerInsights()) +} + +func TestIsAppSignalsEnabled(t *testing.T) { + tests := []struct { + name string + metricNameSpace string + logGroupName string + expectedResult bool + }{ + { + "validAppSignalsEMF", + "AppSignals", + "/aws/appsignals/eks", + true, + }, + { + "invalidAppSignalsLogsGroup", + "AppSignals", + "/nonaws/appsignals/eks", + false, + }, + { + "invalidAppSignalsMetricNamespace", + "NonAppSignals", + "/aws/appsignals/eks", + false, + }, + { + "invalidAppSignalsEMF", + "NonAppSignals", + "/nonaws/appsignals/eks", + false, + }, + { + "defaultConfig", + "", + "", + false, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + if len(tc.metricNameSpace) > 0 { + cfg.Namespace = tc.metricNameSpace + } + if len(tc.logGroupName) > 0 { + cfg.LogGroupName = tc.logGroupName + } + + assert.Equal(t, cfg.IsAppSignalsEnabled(), tc.expectedResult) + }) + } +} diff --git a/exporter/awsemfexporter/datapoint.go b/exporter/awsemfexporter/datapoint.go index 4aba2c5bba50..65f01334b994 100644 --- a/exporter/awsemfexporter/datapoint.go +++ b/exporter/awsemfexporter/datapoint.go @@ -81,15 +81,11 @@ type numberDataPointSlice struct { // histogramDataPointSlice is a wrapper for pmetric.HistogramDataPointSlice type histogramDataPointSlice struct { - // Todo:(khanhntd) Calculate delta value for count and sum value with histogram - // https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18245 deltaMetricMetadata pmetric.HistogramDataPointSlice } type exponentialHistogramDataPointSlice struct { - // TODO: Calculate delta value for count and sum value with exponential histogram - // https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18245 deltaMetricMetadata pmetric.ExponentialHistogramDataPointSlice } @@ -146,16 +142,40 @@ func (dps numberDataPointSlice) CalculateDeltaDatapoints(i int, instrumentationS } // CalculateDeltaDatapoints retrieves the HistogramDataPoint at the given index. -func (dps histogramDataPointSlice) CalculateDeltaDatapoints(i int, instrumentationScopeName string, _ bool, _ *emfCalculators) ([]dataPoint, bool) { +func (dps histogramDataPointSlice) CalculateDeltaDatapoints(i int, instrumentationScopeName string, _ bool, calculators *emfCalculators) ([]dataPoint, bool) { metric := dps.HistogramDataPointSlice.At(i) labels := createLabels(metric.Attributes(), instrumentationScopeName) timestamp := unixNanoToMilliseconds(metric.Timestamp()) + sum := metric.Sum() + count := metric.Count() + + var datapoints []dataPoint + + if dps.adjustToDelta { + var delta interface{} + mKey := aws.NewKey(dps.deltaMetricMetadata, labels) + delta, retained := calculators.summary.Calculate(mKey, summaryMetricEntry{sum, count}, metric.Timestamp().AsTime()) + + // If a delta to the previous data point could not be computed use the current metric value instead + if !retained && dps.retainInitialValueForDelta { + retained = true + delta = summaryMetricEntry{sum, count} + } + + if !retained { + return datapoints, retained + } + summaryMetricDelta := delta.(summaryMetricEntry) + sum = summaryMetricDelta.sum + count = summaryMetricDelta.count + } + return []dataPoint{{ name: dps.metricName, value: &cWMetricStats{ - Count: metric.Count(), - Sum: metric.Sum(), + Count: count, + Sum: sum, Max: metric.Max(), Min: metric.Min(), }, @@ -350,6 +370,8 @@ func getDataPoints(pmd pmetric.Metric, metadata cWMetricMetadata, logger *zap.Lo } case pmetric.MetricTypeHistogram: metric := pmd.Histogram() + // the prometheus histograms from the container insights should be adjusted for delta + metricMetadata.adjustToDelta = metadata.receiver == containerInsightsReceiver dps = histogramDataPointSlice{ metricMetadata, metric.DataPoints(), @@ -368,7 +390,7 @@ func getDataPoints(pmd pmetric.Metric, metadata cWMetricMetadata, logger *zap.Lo // attribute processor) from resource metrics. If it exists, and equals to prometheus, the sum and count will be // converted. // For more information: https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/prometheusreceiver/DESIGN.md#summary - metricMetadata.adjustToDelta = metadata.receiver == prometheusReceiver + metricMetadata.adjustToDelta = metadata.receiver == prometheusReceiver || metadata.receiver == containerInsightsReceiver dps = summaryDataPointSlice{ metricMetadata, metric.DataPoints(), diff --git a/exporter/awsemfexporter/datapoint_test.go b/exporter/awsemfexporter/datapoint_test.go index 03be10da76d2..2c87095dc059 100644 --- a/exporter/awsemfexporter/datapoint_test.go +++ b/exporter/awsemfexporter/datapoint_test.go @@ -383,10 +383,52 @@ func TestCalculateDeltaDatapoints_HistogramDataPointSlice(t *testing.T) { assert.True(t, retained) assert.Equal(t, 1, histogramDatapointSlice.Len()) assert.Equal(t, tc.expectedDatapoint, dps[0]) - }) } +} + +func TestCalculateDeltaDatapoints_HistogramDataPointSlice_Delta(t *testing.T) { + cumulativeDeltaMetricMetadata := generateDeltaMetricMetadata(true, "foo", false) + + histogramDPS := pmetric.NewHistogramDataPointSlice() + histogramDP := histogramDPS.AppendEmpty() + histogramDP.SetCount(uint64(17)) + histogramDP.SetSum(17.13) + histogramDP.SetMin(10) + histogramDP.SetMax(30) + histogramDP.Attributes().PutStr("label1", "value1") + histogramDatapointSlice := histogramDataPointSlice{cumulativeDeltaMetricMetadata, histogramDPS} + emfCalcs := setupEmfCalculators() + defer require.NoError(t, shutdownEmfCalculators(emfCalcs)) + dps, retained := histogramDatapointSlice.CalculateDeltaDatapoints(0, instrLibName, false, emfCalcs) + + assert.False(t, retained) + assert.Equal(t, 1, histogramDatapointSlice.Len()) + assert.Equal(t, 0, len(dps)) + + dps, retained = histogramDatapointSlice.CalculateDeltaDatapoints(0, instrLibName, false, emfCalcs) + assert.True(t, retained) + assert.Equal(t, 1, histogramDatapointSlice.Len()) + assert.Equal(t, dataPoint{ + name: "foo", + value: &cWMetricStats{Sum: 0, Count: 0, Min: 10, Max: 30}, + labels: map[string]string{oTellibDimensionKey: instrLibName, "label1": "value1"}, + }, dps[0]) + + histogramDatapointSlice.HistogramDataPointSlice.At(0).SetCount(uint64(27)) + histogramDatapointSlice.HistogramDataPointSlice.At(0).SetSum(27.27) + histogramDP.SetMin(5) + histogramDP.SetMax(40) + + dps, retained = histogramDatapointSlice.CalculateDeltaDatapoints(0, instrLibName, false, emfCalcs) + assert.True(t, retained) + assert.Equal(t, 1, histogramDatapointSlice.Len()) + assert.Equal(t, dataPoint{ + name: "foo", + value: &cWMetricStats{Sum: 10.14, Count: 10, Min: 5, Max: 40}, + labels: map[string]string{oTellibDimensionKey: instrLibName, "label1": "value1"}, + }, dps[0]) } func TestCalculateDeltaDatapoints_ExponentialHistogramDataPointSlice(t *testing.T) { @@ -600,54 +642,75 @@ func TestCreateLabels(t *testing.T) { func TestGetDataPoints(t *testing.T) { logger := zap.NewNop() - normalDeltraMetricMetadata := generateDeltaMetricMetadata(false, "foo", false) + normalDeltaMetricMetadata := generateDeltaMetricMetadata(false, "foo", false) cumulativeDeltaMetricMetadata := generateDeltaMetricMetadata(true, "foo", false) testCases := []struct { name string - isPrometheusMetrics bool + receiver string metric pmetric.Metrics expectedDatapointSlice dataPoints expectedAttributes map[string]interface{} }{ { name: "Int gauge", - isPrometheusMetrics: false, + receiver: "", metric: generateTestGaugeMetric("foo", intValueType), - expectedDatapointSlice: numberDataPointSlice{normalDeltraMetricMetadata, pmetric.NumberDataPointSlice{}}, + expectedDatapointSlice: numberDataPointSlice{normalDeltaMetricMetadata, pmetric.NumberDataPointSlice{}}, expectedAttributes: map[string]interface{}{"label1": "value1"}, }, { name: "Double sum", - isPrometheusMetrics: false, + receiver: "", metric: generateTestSumMetric("foo", doubleValueType), expectedDatapointSlice: numberDataPointSlice{cumulativeDeltaMetricMetadata, pmetric.NumberDataPointSlice{}}, expectedAttributes: map[string]interface{}{"label1": "value1"}, }, { name: "Histogram", - isPrometheusMetrics: false, + receiver: "", + metric: generateTestHistogramMetric("foo"), + expectedDatapointSlice: histogramDataPointSlice{normalDeltaMetricMetadata, pmetric.HistogramDataPointSlice{}}, + expectedAttributes: map[string]interface{}{"label1": "value1"}, + }, + { + name: "Histogram from ContainerInsights", + receiver: containerInsightsReceiver, metric: generateTestHistogramMetric("foo"), expectedDatapointSlice: histogramDataPointSlice{cumulativeDeltaMetricMetadata, pmetric.HistogramDataPointSlice{}}, expectedAttributes: map[string]interface{}{"label1": "value1"}, }, + { + name: "Histogram from Prometheus", + receiver: prometheusReceiver, + metric: generateTestHistogramMetric("foo"), + expectedDatapointSlice: histogramDataPointSlice{normalDeltaMetricMetadata, pmetric.HistogramDataPointSlice{}}, + expectedAttributes: map[string]interface{}{"label1": "value1"}, + }, { name: "ExponentialHistogram", - isPrometheusMetrics: false, + receiver: "", metric: generateTestExponentialHistogramMetric("foo"), - expectedDatapointSlice: exponentialHistogramDataPointSlice{cumulativeDeltaMetricMetadata, pmetric.ExponentialHistogramDataPointSlice{}}, + expectedDatapointSlice: exponentialHistogramDataPointSlice{normalDeltaMetricMetadata, pmetric.ExponentialHistogramDataPointSlice{}}, expectedAttributes: map[string]interface{}{"label1": "value1"}, }, { name: "Summary from SDK", - isPrometheusMetrics: false, + receiver: "", metric: generateTestSummaryMetric("foo"), - expectedDatapointSlice: summaryDataPointSlice{normalDeltraMetricMetadata, pmetric.SummaryDataPointSlice{}}, + expectedDatapointSlice: summaryDataPointSlice{normalDeltaMetricMetadata, pmetric.SummaryDataPointSlice{}}, expectedAttributes: map[string]interface{}{"label1": "value1"}, }, { name: "Summary from Prometheus", - isPrometheusMetrics: true, + receiver: prometheusReceiver, + metric: generateTestSummaryMetric("foo"), + expectedDatapointSlice: summaryDataPointSlice{cumulativeDeltaMetricMetadata, pmetric.SummaryDataPointSlice{}}, + expectedAttributes: map[string]interface{}{"label1": "value1"}, + }, + { + name: "Summary from ContainerInsights", + receiver: containerInsightsReceiver, metric: generateTestSummaryMetric("foo"), expectedDatapointSlice: summaryDataPointSlice{cumulativeDeltaMetricMetadata, pmetric.SummaryDataPointSlice{}}, expectedAttributes: map[string]interface{}{"label1": "value1"}, @@ -661,13 +724,7 @@ func TestGetDataPoints(t *testing.T) { metadata := generateTestMetricMetadata("namespace", time.Now().UnixNano()/int64(time.Millisecond), "log-group", "log-stream", "cloudwatch-otel", metric.Type()) t.Run(tc.name, func(t *testing.T) { - - if tc.isPrometheusMetrics { - metadata.receiver = prometheusReceiver - } else { - metadata.receiver = "" - } - + metadata.receiver = tc.receiver dps := getDataPoints(metric, metadata, logger) assert.NotNil(t, dps) assert.Equal(t, reflect.TypeOf(tc.expectedDatapointSlice), reflect.TypeOf(dps)) @@ -675,6 +732,7 @@ func TestGetDataPoints(t *testing.T) { case numberDataPointSlice: expectedDPS := tc.expectedDatapointSlice.(numberDataPointSlice) assert.Equal(t, expectedDPS.deltaMetricMetadata, convertedDPS.deltaMetricMetadata) + assert.Equal(t, expectedDPS.adjustToDelta, convertedDPS.adjustToDelta) assert.Equal(t, 1, convertedDPS.Len()) dp := convertedDPS.NumberDataPointSlice.At(0) switch dp.ValueType() { @@ -685,6 +743,8 @@ func TestGetDataPoints(t *testing.T) { } assert.Equal(t, tc.expectedAttributes, dp.Attributes().AsRaw()) case histogramDataPointSlice: + expectedDPS := tc.expectedDatapointSlice.(histogramDataPointSlice) + assert.Equal(t, expectedDPS.adjustToDelta, convertedDPS.adjustToDelta) assert.Equal(t, 1, convertedDPS.Len()) dp := convertedDPS.HistogramDataPointSlice.At(0) assert.Equal(t, 35.0, dp.Sum()) @@ -692,6 +752,8 @@ func TestGetDataPoints(t *testing.T) { assert.Equal(t, []float64{0, 10}, dp.ExplicitBounds().AsRaw()) assert.Equal(t, tc.expectedAttributes, dp.Attributes().AsRaw()) case exponentialHistogramDataPointSlice: + expectedDPS := tc.expectedDatapointSlice.(exponentialHistogramDataPointSlice) + assert.Equal(t, expectedDPS.adjustToDelta, convertedDPS.adjustToDelta) assert.Equal(t, 1, convertedDPS.Len()) dp := convertedDPS.ExponentialHistogramDataPointSlice.At(0) assert.Equal(t, float64(0), dp.Sum()) @@ -703,6 +765,7 @@ func TestGetDataPoints(t *testing.T) { case summaryDataPointSlice: expectedDPS := tc.expectedDatapointSlice.(summaryDataPointSlice) assert.Equal(t, expectedDPS.deltaMetricMetadata, convertedDPS.deltaMetricMetadata) + assert.Equal(t, expectedDPS.adjustToDelta, convertedDPS.adjustToDelta) assert.Equal(t, 1, convertedDPS.Len()) dp := convertedDPS.SummaryDataPointSlice.At(0) assert.Equal(t, 15.0, dp.Sum()) diff --git a/exporter/awsemfexporter/emf_exporter.go b/exporter/awsemfexporter/emf_exporter.go index d00f5f074ee8..6f75a39b6888 100644 --- a/exporter/awsemfexporter/emf_exporter.go +++ b/exporter/awsemfexporter/emf_exporter.go @@ -10,8 +10,10 @@ import ( "strings" "sync" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/google/uuid" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/pdata/pcommon" @@ -27,9 +29,9 @@ const ( outputDestinationCloudWatch = "cloudwatch" outputDestinationStdout = "stdout" - // Pulse EMF config - pulseMetricNamespace = "AWS/APM" - pulseLogGroupNamePrefix = "/aws/apm/" + // AppSignals EMF config + appSignalsMetricNamespace = "AppSignals" + appSignalsLogGroupNamePrefix = "/aws/appsignals/" ) type emfExporter struct { @@ -66,8 +68,8 @@ func newEmfExporter(config *Config, set exporter.CreateSettings) (*emfExporter, config.LogRetention, config.Tags, session, - cwlogs.WithEnabledContainerInsights(isEnhancedContainerInsights(config)), - cwlogs.WithEnabledPulseApm(isPulseApmEnabled(config)), + cwlogs.WithEnabledContainerInsights(config.IsEnhancedContainerInsights()), + cwlogs.WithEnabledAppSignals(config.IsAppSignalsEnabled()), ) collectorIdentifier, err := uuid.NewRandom() @@ -175,7 +177,6 @@ func (emf *emfExporter) pushMetricsData(_ context.Context, md pmetric.Metrics) e } func (emf *emfExporter) getPusher(key cwlogs.PusherKey) cwlogs.Pusher { - var ok bool if _, ok = emf.pusherMap[key]; !ok { emf.pusherMap[key] = cwlogs.NewPusher(key, emf.retryCnt, *emf.svcStructuredLog, emf.config.logger) @@ -194,6 +195,13 @@ func (emf *emfExporter) listPushers() []cwlogs.Pusher { return pushers } +func (emf *emfExporter) start(_ context.Context, host component.Host) error { + if emf.config.MiddlewareID != nil { + awsmiddleware.TryConfigure(emf.config.logger, host, *emf.config.MiddlewareID, awsmiddleware.SDKv1(emf.svcStructuredLog.Handlers())) + } + return nil +} + // shutdown stops the exporter and is invoked during shutdown. func (emf *emfExporter) shutdown(_ context.Context) error { for _, emfPusher := range emf.listPushers() { @@ -216,20 +224,3 @@ func wrapErrorIfBadRequest(err error) error { } return err } - -func isEnhancedContainerInsights(_ *Config) bool { - return false // temporarily disable, also need to rename _config to config - // return config.EnhancedContainerInsights && !config.DisableMetricExtraction -} - -func isPulseApmEnabled(config *Config) bool { - if config.LogGroupName == "" || config.Namespace == "" { - return false - } - - if config.Namespace == pulseMetricNamespace && strings.HasPrefix(config.LogGroupName, pulseLogGroupNamePrefix) { - return true - } - - return false -} diff --git a/exporter/awsemfexporter/emf_exporter_test.go b/exporter/awsemfexporter/emf_exporter_test.go index 9b061d0c8bc9..224a0afb477a 100644 --- a/exporter/awsemfexporter/emf_exporter_test.go +++ b/exporter/awsemfexporter/emf_exporter_test.go @@ -9,10 +9,12 @@ import ( "os" "testing" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter/exportertest" "go.uber.org/zap" @@ -349,70 +351,35 @@ func TestNewEmfExporterWithoutConfig(t *testing.T) { assert.Equal(t, settings.Logger, expCfg.logger) } -func TestIsEnhancedContainerInsights(t *testing.T) { +func TestMiddleware(t *testing.T) { + id := component.NewID("test") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.EnhancedContainerInsights = true - cfg.DisableMetricExtraction = false - assert.False(t, isEnhancedContainerInsights(cfg)) - cfg.EnhancedContainerInsights = false - assert.False(t, isEnhancedContainerInsights(cfg)) - cfg.EnhancedContainerInsights = true - cfg.DisableMetricExtraction = true - assert.False(t, isEnhancedContainerInsights(cfg)) -} - -func TestIsPulseApmEnabled(t *testing.T) { - - tests := []struct { - name string - metricNameSpace string - logGroupName string - expectedResult bool - }{ - { - "validPulseEMF", - "AWS/APM", - "/aws/apm/eks", - true, - }, - { - "invalidPulseLogsGroup", - "AWS/APM", - "/nonaws/apm/eks", - false, - }, - { - "invalidPulseMetricNamespace", - "NonAWS/APM", - "/aws/apm/eks", - false, - }, - { - "invalidPulseEMF", - "NonAWS/APM", - "/nonaws/apm/eks", - false, - }, - { - "defaultConfig", - "", - "", - false, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - if len(tc.metricNameSpace) > 0 { - cfg.Namespace = tc.metricNameSpace - } - if len(tc.logGroupName) > 0 { - cfg.LogGroupName = tc.logGroupName - } - - assert.Equal(t, isPulseApmEnabled(cfg), tc.expectedResult) - }) - } + expCfg := factory.CreateDefaultConfig().(*Config) + expCfg.Region = "us-west-2" + expCfg.MaxRetries = 0 + expCfg.MiddlewareID = &id + handler := new(awsmiddleware.MockHandler) + handler.On("ID").Return("test") + handler.On("Position").Return(awsmiddleware.After) + handler.On("HandleRequest", mock.Anything, mock.Anything) + handler.On("HandleResponse", mock.Anything, mock.Anything) + middleware := new(awsmiddleware.MockMiddlewareExtension) + middleware.On("Handlers").Return([]awsmiddleware.RequestHandler{handler}, []awsmiddleware.ResponseHandler{handler}) + extensions := map[component.ID]component.Component{id: middleware} + exp, err := newEmfExporter(expCfg, exportertest.NewNopCreateSettings()) + assert.Nil(t, err) + assert.NotNil(t, exp) + host := new(awsmiddleware.MockExtensionsHost) + host.On("GetExtensions").Return(extensions) + assert.NoError(t, exp.start(ctx, host)) + md := generateTestMetrics(testMetric{ + metricNames: []string{"metric_1", "metric_2"}, + metricValues: [][]float64{{100}, {4}}, + }) + require.Error(t, exp.pushMetricsData(ctx, md)) + require.NoError(t, exp.shutdown(ctx)) + handler.AssertCalled(t, "HandleRequest", mock.Anything, mock.Anything) + handler.AssertCalled(t, "HandleResponse", mock.Anything, mock.Anything) } diff --git a/exporter/awsemfexporter/factory.go b/exporter/awsemfexporter/factory.go index d0cf78a5dd2d..ba6837f547f3 100644 --- a/exporter/awsemfexporter/factory.go +++ b/exporter/awsemfexporter/factory.go @@ -65,6 +65,7 @@ func createMetricsExporter(ctx context.Context, params exporter.CreateSettings, params, config, emfExp.pushMetricsData, + exporterhelper.WithStart(emfExp.start), exporterhelper.WithShutdown(emfExp.shutdown), ) if err != nil { diff --git a/exporter/awsemfexporter/go.mod b/exporter/awsemfexporter/go.mod index 289d59ccf239..643d2e6dd8e2 100644 --- a/exporter/awsemfexporter/go.mod +++ b/exporter/awsemfexporter/go.mod @@ -3,7 +3,8 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsemf go 1.20 require ( - github.com/aws/aws-sdk-go v1.45.2 + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 + github.com/aws/aws-sdk-go v1.45.24 github.com/google/uuid v1.3.1 github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil v0.84.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs v0.84.0 @@ -25,6 +26,8 @@ require ( require ( github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20230818193829-04a761abd409 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.4 // indirect @@ -83,3 +86,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil replace github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws => ../../override/aws + +replace github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => ../../extension/awsmiddleware diff --git a/exporter/awsemfexporter/go.sum b/exporter/awsemfexporter/go.sum index 076b468b7b28..1d4288be61b3 100644 --- a/exporter/awsemfexporter/go.sum +++ b/exporter/awsemfexporter/go.sum @@ -12,18 +12,31 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= -github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.24 h1:TZx/CizkmCQn8Rtsb11iLYutEQVGK5PK9wAhwouELBo= +github.com/aws/aws-sdk-go v1.45.24/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -106,6 +119,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/exporter/awsemfexporter/metric_translator.go b/exporter/awsemfexporter/metric_translator.go index ea795bd2a3ef..d2128c196f52 100644 --- a/exporter/awsemfexporter/metric_translator.go +++ b/exporter/awsemfexporter/metric_translator.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" "go.opentelemetry.io/collector/pdata/pmetric" @@ -27,6 +28,7 @@ const ( singleDimensionRollupOnly = "SingleDimensionRollupOnly" prometheusReceiver = "prometheus" + containerInsightsReceiver = "awscontainerinsight" attributeReceiver = "receiver" fieldPrometheusMetricType = "prom_metric_type" ) @@ -124,6 +126,14 @@ func (mt metricTranslator) translateOTelToGroupedMetric(rm pmetric.ResourceMetri if receiver, ok := rm.Resource().Attributes().Get(attributeReceiver); ok { metricReceiver = receiver.Str() } + + if serviceName, ok := rm.Resource().Attributes().Get("service.name"); ok { + if strings.HasPrefix(serviceName.Str(), "containerInsightsKubeAPIServerScraper") { + // the prometheus metrics that come from the container insight receiver need to be clearly tagged as coming from container insights + metricReceiver = containerInsightsReceiver + } + } + for j := 0; j < ilms.Len(); j++ { ilm := ilms.At(j) if ilm.Scope().Name() != "" { diff --git a/exporter/awsemfexporter/metric_translator_test.go b/exporter/awsemfexporter/metric_translator_test.go index 5e5b7b3997be..92aec3885a85 100644 --- a/exporter/awsemfexporter/metric_translator_test.go +++ b/exporter/awsemfexporter/metric_translator_test.go @@ -5,6 +5,7 @@ package awsemfexporter import ( "fmt" + "math" "reflect" "sort" "testing" @@ -22,7 +23,13 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/occonventions" ) +const defaultNumberOfTestMetrics = 3 + func createTestResourceMetrics() pmetric.ResourceMetrics { + return createTestResourceMetricsHelper(defaultNumberOfTestMetrics) +} + +func createTestResourceMetricsHelper(numMetrics int) pmetric.ResourceMetrics { rm := pmetric.NewResourceMetrics() rm.Resource().Attributes().PutStr(occonventions.AttributeExporterVersion, "SomeVersion") rm.Resource().Attributes().PutStr(conventions.AttributeServiceName, "myServiceName") @@ -79,7 +86,7 @@ func createTestResourceMetrics() pmetric.ResourceMetrics { q2.SetQuantile(1) q2.SetValue(5) - for i := 0; i < 2; i++ { + for i := 1; i < numMetrics; i++ { m = sm.Metrics().AppendEmpty() m.SetName("spanCounter") m.SetDescription("Counting all the spans") @@ -268,6 +275,10 @@ func TestTranslateOtToGroupedMetric(t *testing.T) { noNamespaceMetric.Resource().Attributes().Remove(conventions.AttributeServiceNamespace) noNamespaceMetric.Resource().Attributes().Remove(conventions.AttributeServiceName) + // need to have 1 more metric than the default because the first is not going to be retained because it is a delta metric + containerInsightMetric := createTestResourceMetricsHelper(defaultNumberOfTestMetrics + 1) + containerInsightMetric.Resource().Attributes().PutStr(conventions.AttributeServiceName, "containerInsightsKubeAPIServerScraper") + counterSumMetrics := map[string]*metricInfo{ "spanCounter": { value: float64(1), @@ -304,6 +315,7 @@ func TestTranslateOtToGroupedMetric(t *testing.T) { counterLabels map[string]string timerLabels map[string]string expectedNamespace string + expectedReceiver string }{ { "w/ instrumentation library and namespace", @@ -318,6 +330,7 @@ func TestTranslateOtToGroupedMetric(t *testing.T) { "spanName": "testSpan", }, "myServiceNS/myServiceName", + "prometheus", }, { "w/o instrumentation library, w/ namespace", @@ -330,6 +343,7 @@ func TestTranslateOtToGroupedMetric(t *testing.T) { "spanName": "testSpan", }, "myServiceNS/myServiceName", + prometheusReceiver, }, { "w/o instrumentation library and namespace", @@ -342,20 +356,43 @@ func TestTranslateOtToGroupedMetric(t *testing.T) { "spanName": "testSpan", }, defaultNamespace, + prometheusReceiver, + }, + { + "container insights receiver", + containerInsightMetric, + map[string]string{ + "isItAnError": "false", + "spanName": "testSpan", + }, + map[string]string{ + (oTellibDimensionKey): "cloudwatch-lib", + "spanName": "testSpan", + }, + "myServiceNS/containerInsightsKubeAPIServerScraper", + containerInsightsReceiver, }, } for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { - groupedMetrics := make(map[interface{}]*groupedMetric) err := translator.translateOTelToGroupedMetric(tc.metric, groupedMetrics, config) assert.Nil(t, err) assert.NotNil(t, groupedMetrics) - assert.Equal(t, 3, len(groupedMetrics)) + assert.Equal(t, defaultNumberOfTestMetrics, len(groupedMetrics)) for _, v := range groupedMetrics { assert.Equal(t, tc.expectedNamespace, v.metadata.namespace) + assert.Equal(t, tc.expectedReceiver, v.metadata.receiver) + + for _, metric := range v.metrics { + if mv, ok := metric.value.(float64); ok { + // round the metrics, the floats can get off by a very small amount + metric.value = math.Round(mv*100000) / 100000 + } + } + switch { case v.metadata.metricDataType == pmetric.MetricTypeSum: assert.Equal(t, 2, len(v.metrics)) @@ -1456,22 +1493,22 @@ func TestGroupedMetricToCWMeasurementsWithFilters(t *testing.T) { MetricNameSelectors: []string{"metric(1|3)"}, }, }, []cWMeasurement{ - { - Namespace: namespace, - Dimensions: [][]string{{}}, - Metrics: []map[string]string{ - { - "Name": "metric1", - "Unit": "Count", - }, - { - "Name": "metric3", - "Unit": "Seconds", + { + Namespace: namespace, + Dimensions: [][]string{{}}, + Metrics: []map[string]string{ + { + "Name": "metric1", + "Unit": "Count", + }, + { + "Name": "metric3", + "Unit": "Seconds", + }, }, }, }, }, - }, { "label matchers", []*MetricDeclaration{ diff --git a/exporter/awsxrayexporter/awsxray.go b/exporter/awsxrayexporter/awsxray.go index 5d68b99fba3b..3454b5e663a0 100644 --- a/exporter/awsxrayexporter/awsxray.go +++ b/exporter/awsxrayexporter/awsxray.go @@ -7,6 +7,7 @@ import ( "context" "errors" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/xray" "go.opentelemetry.io/collector/component" @@ -84,8 +85,11 @@ func newTracesExporter( } return err }, - exporterhelper.WithStart(func(context.Context, component.Host) error { + exporterhelper.WithStart(func(_ context.Context, host component.Host) error { sender.Start() + if cfg.MiddlewareID != nil { + awsmiddleware.TryConfigure(logger, host, *cfg.MiddlewareID, awsmiddleware.SDKv1(xrayClient.Handlers())) + } return nil }), exporterhelper.WithShutdown(func(context.Context) error { diff --git a/exporter/awsxrayexporter/awsxray_test.go b/exporter/awsxrayexporter/awsxray_test.go index 3f7fc4d6bdf2..895a8fecfacc 100644 --- a/exporter/awsxrayexporter/awsxray_test.go +++ b/exporter/awsxrayexporter/awsxray_test.go @@ -11,7 +11,9 @@ import ( "testing" "time" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" @@ -91,6 +93,30 @@ func TestTelemetryEnabled(t *testing.T) { assert.EqualValues(t, 1, *got.BackendConnectionErrors.HTTPCode4XXCount) } +func TestMiddleware(t *testing.T) { + id := component.NewID("test") + cfg := generateConfig(t) + cfg.MiddlewareID = &id + handler := new(awsmiddleware.MockHandler) + handler.On("ID").Return("test") + handler.On("Position").Return(awsmiddleware.After) + handler.On("HandleRequest", mock.Anything, mock.Anything) + handler.On("HandleResponse", mock.Anything, mock.Anything) + middleware := new(awsmiddleware.MockMiddlewareExtension) + middleware.On("Handlers").Return([]awsmiddleware.RequestHandler{handler}, []awsmiddleware.ResponseHandler{handler}) + extensions := map[component.ID]component.Component{id: middleware} + traceExporter := initializeTracesExporter(t, cfg, telemetrytest.NewNopRegistry()) + host := new(awsmiddleware.MockExtensionsHost) + host.On("GetExtensions").Return(extensions) + ctx := context.Background() + assert.NoError(t, traceExporter.Start(ctx, host)) + td := constructSpanData() + err := traceExporter.ConsumeTraces(ctx, td) + assert.Error(t, err) + handler.AssertCalled(t, "HandleRequest", mock.Anything, mock.Anything) + handler.AssertCalled(t, "HandleResponse", mock.Anything, mock.Anything) +} + func BenchmarkForTracesExporter(b *testing.B) { traceExporter := initializeTracesExporter(b, generateConfig(b), telemetrytest.NewNopRegistry()) for i := 0; i < b.N; i++ { diff --git a/exporter/awsxrayexporter/config.go b/exporter/awsxrayexporter/config.go index 294ace13346f..5f97907ce8e1 100644 --- a/exporter/awsxrayexporter/config.go +++ b/exporter/awsxrayexporter/config.go @@ -4,6 +4,8 @@ package awsxrayexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter" import ( + "go.opentelemetry.io/collector/component" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray/telemetry" ) @@ -24,6 +26,9 @@ type Config struct { LogGroupNames []string `mapstructure:"aws_log_groups"` // TelemetryConfig contains the options for telemetry collection. TelemetryConfig telemetry.Config `mapstructure:"telemetry,omitempty"` + // MiddlewareID is an ID for an extension that can be used to configure the + // AWS client. + MiddlewareID *component.ID `mapstructure:"middleware,omitempty"` // skipTimestampValidation if enabled, will skip timestamp validation logic on the trace ID skipTimestampValidation bool diff --git a/exporter/awsxrayexporter/go.mod b/exporter/awsxrayexporter/go.mod index 702a5471021e..66667f2d2555 100644 --- a/exporter/awsxrayexporter/go.mod +++ b/exporter/awsxrayexporter/go.mod @@ -3,6 +3,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxra go 1.20 require ( + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 github.com/aws/aws-sdk-go v1.45.2 github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil v0.84.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray v0.84.0 @@ -20,12 +21,15 @@ require ( require ( github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20230818193829-04a761abd409 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/knadh/koanf v1.5.0 // indirect @@ -36,6 +40,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.84.1-0.20230908201109-ab3d6c5b6470 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.84.1-0.20230908201109-ab3d6c5b6470 // indirect @@ -74,3 +79,5 @@ retract ( replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest + +replace github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => ../../extension/awsmiddleware diff --git a/exporter/awsxrayexporter/go.sum b/exporter/awsxrayexporter/go.sum index f4a854541bb1..693cda6fe2c3 100644 --- a/exporter/awsxrayexporter/go.sum +++ b/exporter/awsxrayexporter/go.sum @@ -15,15 +15,28 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -106,9 +119,12 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= diff --git a/exporter/awsxrayexporter/internal/translator/segment.go b/exporter/awsxrayexporter/internal/translator/segment.go index efda3ed22f2e..0ff0199016e3 100644 --- a/exporter/awsxrayexporter/internal/translator/segment.go +++ b/exporter/awsxrayexporter/internal/translator/segment.go @@ -155,7 +155,8 @@ func MakeDependencySubsegmentForLocalRootDependencySpan(span ptrace.Span, resour } if myAwsRemoteService, ok := span.Attributes().Get(awsRemoteService); ok { - dependencySubsegment.Name = awsxray.String(myAwsRemoteService.Str()) + subsegmentName := myAwsRemoteService.Str() + dependencySubsegment.Name = awsxray.String(trimAwsSdkPrefix(subsegmentName, span)) } return dependencySubsegment, err @@ -370,9 +371,7 @@ func MakeSegment(span ptrace.Span, resource pcommon.Resource, indexedAttrs []str if remoteServiceName, ok := attributes.Get(awsRemoteService); ok { name = remoteServiceName.Str() // only strip the prefix for AWS spans - if isAwsSdkSpan(span) && strings.HasPrefix(name, "AWS.SDK.") { - name = strings.TrimPrefix(name, "AWS.SDK.") - } + name = trimAwsSdkPrefix(name, span) } } @@ -752,3 +751,10 @@ func fixAnnotationKey(key string) string { } }, key) } + +func trimAwsSdkPrefix(name string, span ptrace.Span) string { + if isAwsSdkSpan(span) && strings.HasPrefix(name, "AWS.SDK.") { + return strings.TrimPrefix(name, "AWS.SDK.") + } + return name +} diff --git a/exporter/awsxrayexporter/internal/translator/segment_test.go b/exporter/awsxrayexporter/internal/translator/segment_test.go index dd5978c61309..18fdd70771ee 100644 --- a/exporter/awsxrayexporter/internal/translator/segment_test.go +++ b/exporter/awsxrayexporter/internal/translator/segment_test.go @@ -1333,6 +1333,39 @@ func TestLocalRootClient(t *testing.T) { assert.Equal(t, segments[0].EndTime, segments[1].EndTime) } +func TestLocalRootClientAwsServiceMetrics(t *testing.T) { + spanName := "SQS ReceiveMessage" + resource := getBasicResource() + + parentSpanID := newSegmentID() + + attributes := getBasicAttributes() + attributes[awsSpanKind] = "LOCAL_ROOT" + attributes[conventions.AttributeRPCSystem] = "aws-api" + attributes[conventions.AttributeHTTPMethod] = "POST" + attributes[conventions.AttributeHTTPScheme] = "https" + attributes[conventions.AttributeRPCService] = "SQS" + attributes[awsRemoteService] = "AWS.SDK.SQS" + + span := constructClientSpan(parentSpanID, spanName, 200, "OK", attributes) + + spanLink := span.Links().AppendEmpty() + spanLink.SetTraceID(newTraceID()) + spanLink.SetSpanID(newSegmentID()) + + segments, err := MakeSegmentsFromSpan(span, resource, []string{awsRemoteService, "myAnnotationKey"}, false, nil, false) + + assert.NotNil(t, segments) + assert.Equal(t, 2, len(segments)) + assert.Nil(t, err) + + subsegment := segments[0] + + assert.Equal(t, "subsegment", *subsegment.Type) + assert.Equal(t, "SQS", *subsegment.Name) + assert.Equal(t, "aws", *subsegment.Namespace) +} + func TestLocalRootProducer(t *testing.T) { spanName := "destination operation" resource := getBasicResource() diff --git a/extension/awsmiddleware/Makefile b/extension/awsmiddleware/Makefile new file mode 100644 index 000000000000..ded7a36092dc --- /dev/null +++ b/extension/awsmiddleware/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/extension/awsmiddleware/README.md b/extension/awsmiddleware/README.md new file mode 100644 index 000000000000..9a3f7a2f0af0 --- /dev/null +++ b/extension/awsmiddleware/README.md @@ -0,0 +1,32 @@ +# AWS Middleware + +An AWS middleware extension provides request and/or response handlers that can be configured on AWS SDK v1/v2 clients. +Other components can configure their AWS SDK clients using `awsmiddleware.GetConfigurer` and passing the `SDKv1` or `SDKv2` +options into the `Configure` function available on the `Configurer`. + +The `awsmiddleware.Extension` interface extends `component.Extension` by adding the following method: +``` +Handlers() ([]RequestHandler, []ResponseHandler) +``` + +The `awsmiddleware.RequestHandler` interface contains the following methods: +``` +ID() string +Position() HandlerPosition +HandleRequest(ctx context.Context, r *http.Request) +``` + +The `awsmiddleware.ResponseHandler` interface contains the following methods: +``` +ID() string +Position() HandlerPosition +HandleResponse(ctx context.Context, r *http.Response) +``` + +- `ID` uniquely identifies a handler. Middleware will fail if there is clashing +- `Position` determines whether the handler is appended to the front or back of the existing list. Insertion is done +in the order of the handlers provided. +- `HandleRequest/Response` provides a hook to handle the request/response before and after they've been sent along +with the context. + +There are a functions available that can be used to extract metadata from the context. diff --git a/extension/awsmiddleware/config.go b/extension/awsmiddleware/config.go new file mode 100644 index 000000000000..1f211596d8da --- /dev/null +++ b/extension/awsmiddleware/config.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + +import ( + "fmt" + + "go.opentelemetry.io/collector/component" +) + +// getMiddleware retrieves the extension implementing Middleware based on the middlewareID. +func getMiddleware(extensions map[component.ID]component.Component, middlewareID component.ID) (Middleware, error) { + if extension, found := extensions[middlewareID]; found { + if middleware, ok := extension.(Middleware); ok { + return middleware, nil + } + return nil, errNotMiddleware + } + return nil, fmt.Errorf("failed to resolve AWS middleware %q: %w", middlewareID, errNotFound) +} + +// GetConfigurer retrieves the extension implementing Middleware based on the middlewareID and +// wraps it in a Configurer. +func GetConfigurer(extensions map[component.ID]component.Component, middlewareID component.ID) (*Configurer, error) { + middleware, err := getMiddleware(extensions, middlewareID) + if err != nil { + return nil, err + } + return NewConfigurer(middleware.Handlers()), nil +} diff --git a/extension/awsmiddleware/config_test.go b/extension/awsmiddleware/config_test.go new file mode 100644 index 000000000000..fbdd773dbfbf --- /dev/null +++ b/extension/awsmiddleware/config_test.go @@ -0,0 +1,51 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension/extensiontest" +) + +func TestGetConfigurer(t *testing.T) { + id := component.NewID("test") + nopExtension, err := extensiontest.NewNopBuilder().Create(context.Background(), extensiontest.NewNopCreateSettings()) + require.Error(t, err) + middlewareExtension := new(MockMiddlewareExtension) + middlewareExtension.On("Handlers").Return(nil, nil) + testCases := map[string]struct { + extensions map[component.ID]component.Component + wantErr error + }{ + "WithNoExtensions": { + extensions: map[component.ID]component.Component{}, + wantErr: errNotFound, + }, + "WithNonMiddlewareExtension": { + extensions: map[component.ID]component.Component{id: nopExtension}, + wantErr: errNotMiddleware, + }, + "WithMiddlewareExtension": { + extensions: map[component.ID]component.Component{id: middlewareExtension}, + }, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + got, err := GetConfigurer(testCase.extensions, id) + if testCase.wantErr != nil { + assert.Error(t, err) + assert.ErrorIs(t, err, testCase.wantErr) + assert.Nil(t, got) + } else { + assert.NoError(t, err) + assert.NotNil(t, got) + } + }) + } +} diff --git a/extension/awsmiddleware/doc.go b/extension/awsmiddleware/doc.go new file mode 100644 index 000000000000..1426ff222520 --- /dev/null +++ b/extension/awsmiddleware/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package awsmiddleware defines an extension interface providing request and response handlers that can be +// configured on AWS SDK clients. +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" diff --git a/extension/awsmiddleware/go.mod b/extension/awsmiddleware/go.mod new file mode 100644 index 000000000000..faddf7ab3f23 --- /dev/null +++ b/extension/awsmiddleware/go.mod @@ -0,0 +1,53 @@ +module github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware + +go 1.20 + +require ( + github.com/aws/aws-sdk-go v1.45.2 + github.com/aws/aws-sdk-go-v2 v1.21.2 + github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 + github.com/aws/smithy-go v1.15.0 + github.com/google/uuid v1.3.1 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/collector/component v0.84.1-0.20230908201109-ab3d6c5b6470 + go.opentelemetry.io/collector/extension v0.84.1-0.20230908201109-ab3d6c5b6470 + go.uber.org/zap v1.25.0 +) + +require ( + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/jmespath/go-jmespath v0.4.0 // 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.0.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.84.1-0.20230908201109-ab3d6c5b6470 // indirect + go.opentelemetry.io/collector/confmap v0.84.1-0.20230908201109-ab3d6c5b6470 // indirect + go.opentelemetry.io/collector/featuregate v1.0.0-rcv0014.0.20230908201109-ab3d6c5b6470 // indirect + go.opentelemetry.io/collector/pdata v1.0.0-rcv0014.0.20230908201109-ab3d6c5b6470 // indirect + go.opentelemetry.io/otel v1.17.0 // indirect + go.opentelemetry.io/otel/metric v1.17.0 // indirect + go.opentelemetry.io/otel/trace v1.17.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/grpc v1.57.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/extension/awsmiddleware/go.sum b/extension/awsmiddleware/go.sum new file mode 100644 index 000000000000..2442edea841d --- /dev/null +++ b/extension/awsmiddleware/go.sum @@ -0,0 +1,163 @@ +github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= +github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= +github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/collector/component v0.84.1-0.20230908201109-ab3d6c5b6470 h1:Bh0c9F7Xc2OGGzBPqr8qSf0lTIzhDUES61TRd0UXZsc= +go.opentelemetry.io/collector/component v0.84.1-0.20230908201109-ab3d6c5b6470/go.mod h1:RJcjSU7iI/P4qMvnhvKJU73hkLp8UvPych4LiL8SMhY= +go.opentelemetry.io/collector/config/configtelemetry v0.84.1-0.20230908201109-ab3d6c5b6470 h1:KM7Uu6d35GfDEhtB3e0/fuV31gCIk2qqT2Ppq1FLAqA= +go.opentelemetry.io/collector/config/configtelemetry v0.84.1-0.20230908201109-ab3d6c5b6470/go.mod h1:+LAXM5WFMW/UbTlAuSs6L/W72WC+q8TBJt/6z39FPOU= +go.opentelemetry.io/collector/confmap v0.84.1-0.20230908201109-ab3d6c5b6470 h1:tteTmxiBmllwy6O8gH9gbedc1IjjFfnaGI0/YZY7CYc= +go.opentelemetry.io/collector/confmap v0.84.1-0.20230908201109-ab3d6c5b6470/go.mod h1:/SNHqYkLagF0TjBjQyCy2Gt3ZF6hTK8VKbaan/ZHuJs= +go.opentelemetry.io/collector/extension v0.84.1-0.20230908201109-ab3d6c5b6470 h1:fzzHomPcNWbf6k6fmehHnyz0T1ti96TjP+G5NXfz1og= +go.opentelemetry.io/collector/extension v0.84.1-0.20230908201109-ab3d6c5b6470/go.mod h1:FPOAdIEsz6PA48Xo3oaQxioBPjaMv9Kaj187cqyMqFU= +go.opentelemetry.io/collector/featuregate v1.0.0-rcv0014.0.20230908201109-ab3d6c5b6470 h1:W4D/cCSsvMaNZOk17ak19okMXb2iq07zaeABRlyALzQ= +go.opentelemetry.io/collector/featuregate v1.0.0-rcv0014.0.20230908201109-ab3d6c5b6470/go.mod h1:fLmJMf1AoHttkF8p5oJAc4o5ZpHu8yO5XYJ7gbLCLzo= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0014.0.20230908201109-ab3d6c5b6470 h1:wGdaYUS9S4Ot+WxmsxpagV5+GypvGgX51lR2jT9Kw10= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0014.0.20230908201109-ab3d6c5b6470/go.mod h1:En5jH+YI2KAJhyfpXvjw00OnhWEgGw4Ka0wsBZBWIEo= +go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= +go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= +go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= +go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= +go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= +go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +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.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +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/sync v0.0.0-20220722155255-886fb9371eb4/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.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +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-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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/awsmiddleware/helper.go b/extension/awsmiddleware/helper.go new file mode 100644 index 000000000000..7b47fbd197e8 --- /dev/null +++ b/extension/awsmiddleware/helper.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + +import ( + "go.opentelemetry.io/collector/component" + "go.uber.org/zap" +) + +// TryConfigure is a helper function that will try to get the extension and configure the provided AWS SDK with it. +func TryConfigure(logger *zap.Logger, host component.Host, middlewareID component.ID, sdkVersion SDKVersion) { + if c, err := GetConfigurer(host.GetExtensions(), middlewareID); err != nil { + logger.Error("Unable to find AWS Middleware extension", zap.Error(err)) + } else if err = c.Configure(sdkVersion); err != nil { + logger.Warn("Unable to configure middleware on AWS client", zap.Error(err)) + } else { + logger.Debug("Configured middleware on AWS client", zap.String("middleware", middlewareID.String())) + } +} diff --git a/extension/awsmiddleware/helper_test.go b/extension/awsmiddleware/helper_test.go new file mode 100644 index 000000000000..bd5f5adc535a --- /dev/null +++ b/extension/awsmiddleware/helper_test.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware + +import ( + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +func TestTryConfigure(t *testing.T) { + testCases := []SDKVersion{SDKv1(&request.Handlers{}), SDKv2(&aws.Config{})} + for _, testCase := range testCases { + id := component.NewID("test") + host := new(MockExtensionsHost) + host.On("GetExtensions").Return(nil).Once() + core, observed := observer.New(zap.DebugLevel) + logger := zap.New(core) + TryConfigure(logger, host, id, testCase) + require.Len(t, observed.FilterLevelExact(zap.ErrorLevel).TakeAll(), 1) + + extensions := map[component.ID]component.Component{} + host.On("GetExtensions").Return(extensions) + handler := new(MockHandler) + handler.On("ID").Return("test") + handler.On("Position").Return(HandlerPosition(-1)) + extension := new(MockMiddlewareExtension) + extension.On("Handlers").Return([]RequestHandler{handler}, nil).Once() + extensions[id] = extension + TryConfigure(logger, host, id, testCase) + require.Len(t, observed.FilterLevelExact(zap.WarnLevel).TakeAll(), 1) + + extension.On("Handlers").Return(nil, nil).Once() + TryConfigure(logger, host, id, testCase) + require.Len(t, observed.FilterLevelExact(zap.DebugLevel).TakeAll(), 1) + } +} diff --git a/extension/awsmiddleware/middleware.go b/extension/awsmiddleware/middleware.go new file mode 100644 index 000000000000..085ea4c591b0 --- /dev/null +++ b/extension/awsmiddleware/middleware.go @@ -0,0 +1,203 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + +import ( + "context" + "encoding" + "errors" + "fmt" + "net/http" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/smithy-go/middleware" + "go.opentelemetry.io/collector/extension" +) + +var ( + errNotFound = errors.New("middleware not found") + errNotMiddleware = errors.New("extension is not an AWS middleware") + errInvalidHandler = errors.New("invalid handler") + errUnsupportedPosition = errors.New("unsupported position") + errUnsupportedVersion = errors.New("unsupported SDK version") +) + +// HandlerPosition is the relative position of a handler used during insertion. +type HandlerPosition int + +var _ encoding.TextMarshaler = (*HandlerPosition)(nil) +var _ encoding.TextUnmarshaler = (*HandlerPosition)(nil) + +const ( + After HandlerPosition = iota + Before + + afterStr = "after" + beforeStr = "before" +) + +// String returns the string representation of the position. +// Returns an empty string if position is unsupported. +func (h HandlerPosition) String() string { + switch h { + case Before: + return beforeStr + case After: + return afterStr + default: + return "" + } +} + +// MarshalText converts the position into a byte slice. +// Returns an error if unsupported. +func (h HandlerPosition) MarshalText() (text []byte, err error) { + s := h.String() + if s == "" { + return nil, fmt.Errorf("%w: %[2]T(%[2]d)", errUnsupportedPosition, h) + } + return []byte(h.String()), nil +} + +// UnmarshalText converts the string into a position. Returns an error +// if unsupported. +func (h *HandlerPosition) UnmarshalText(text []byte) error { + switch s := string(text); s { + case afterStr: + *h = After + case beforeStr: + *h = Before + default: + return fmt.Errorf("%w: %s", errUnsupportedPosition, s) + } + return nil +} + +// handlerConfig is used to differentiate between handlers and determine +// relative positioning within their groups. +type handlerConfig interface { + // ID must be unique. It cannot clash with existing middleware. + ID() string + // Position to insert the handler. + Position() HandlerPosition +} + +// RequestHandler allows for custom processing of requests. +type RequestHandler interface { + handlerConfig + HandleRequest(ctx context.Context, r *http.Request) +} + +// ResponseHandler allows for custom processing of responses. +type ResponseHandler interface { + handlerConfig + HandleResponse(ctx context.Context, r *http.Response) +} + +// Middleware defines the request and response handlers to be configured +// on AWS Clients. +type Middleware interface { + Handlers() ([]RequestHandler, []ResponseHandler) +} + +// Extension is an extension that implements Middleware. +type Extension interface { + extension.Extension + Middleware +} + +// Configurer provides functions for applying request/response handlers +// to the AWS SDKs. +type Configurer struct { + requestHandlers []RequestHandler + responseHandlers []ResponseHandler +} + +// NewConfigurer sets the request/response handlers. +func NewConfigurer(requestHandlers []RequestHandler, responseHandlers []ResponseHandler) *Configurer { + return &Configurer{requestHandlers: requestHandlers, responseHandlers: responseHandlers} +} + +// Configure configures the handlers on the provided AWS SDK. +func (c Configurer) Configure(sdkVersion SDKVersion) error { + switch v := sdkVersion.(type) { + case sdkVersion1: + return c.configureSDKv1(v.handlers) + case sdkVersion2: + return c.configureSDKv2(v.cfg) + default: + return fmt.Errorf("%w: %T", errUnsupportedVersion, v) + } +} + +// configureSDKv1 adds middleware to the AWS SDK v1. Request handlers are added to the +// Build handler list and response handlers are added to the ValidateResponse handler list. +// Build will only be run once per request, but if there are errors, ValidateResponse will +// be run again for each configured retry. +func (c Configurer) configureSDKv1(handlers *request.Handlers) error { + var errs error + for _, handler := range c.requestHandlers { + if err := appendHandler(&handlers.Build, namedRequestHandler(handler), handler.Position()); err != nil { + errs = errors.Join(errs, fmt.Errorf("%w (%q): %w", errInvalidHandler, handler.ID(), err)) + } + } + for _, handler := range c.responseHandlers { + if err := appendHandler(&handlers.ValidateResponse, namedResponseHandler(handler), handler.Position()); err != nil { + errs = errors.Join(errs, fmt.Errorf("%w (%q): %w", errInvalidHandler, handler.ID(), err)) + } + } + return errs +} + +// configureSDKv2 adds middleware to the AWS SDK v2. Request handlers are added to the +// Build step and response handlers are added to the Deserialize step. +func (c Configurer) configureSDKv2(config *aws.Config) error { + var errs error + for _, handler := range c.requestHandlers { + relativePosition, err := toRelativePosition(handler.Position()) + if err != nil { + errs = errors.Join(errs, fmt.Errorf("%w (%q): %w", errInvalidHandler, handler.ID(), err)) + continue + } + config.APIOptions = append(config.APIOptions, withBuildOption(&requestMiddleware{RequestHandler: handler}, relativePosition)) + } + for _, handler := range c.responseHandlers { + relativePosition, err := toRelativePosition(handler.Position()) + if err != nil { + errs = errors.Join(errs, fmt.Errorf("%w (%q): %w", errInvalidHandler, handler.ID(), err)) + continue + } + config.APIOptions = append(config.APIOptions, withDeserializeOption(&responseMiddleware{ResponseHandler: handler}, relativePosition)) + } + return errs +} + +// appendHandler adds the handler to the list based on the position. +func appendHandler(handlerList *request.HandlerList, handler request.NamedHandler, position HandlerPosition) error { + relativePosition, err := toRelativePosition(position) + if err != nil { + return err + } + switch relativePosition { + case middleware.Before: + handlerList.PushFrontNamed(handler) + case middleware.After: + handlerList.PushBackNamed(handler) + } + return nil +} + +// toRelativePosition maps the HandlerPosition to a middleware.RelativePosition. It also validates that +// the HandlerPosition provided is supported and returns an errUnsupportedPosition if it isn't. +func toRelativePosition(position HandlerPosition) (middleware.RelativePosition, error) { + switch position { + case Before: + return middleware.Before, nil + case After: + return middleware.After, nil + default: + return -1, fmt.Errorf("%w: %s", errUnsupportedPosition, position) + } +} diff --git a/extension/awsmiddleware/middleware_test.go b/extension/awsmiddleware/middleware_test.go new file mode 100644 index 000000000000..fece342893d4 --- /dev/null +++ b/extension/awsmiddleware/middleware_test.go @@ -0,0 +1,291 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + awsv2 "github.com/aws/aws-sdk-go-v2/aws" + s3v2 "github.com/aws/aws-sdk-go-v2/service/s3" + awsv1 "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/awstesting" + s3v1 "github.com/aws/aws-sdk-go/service/s3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +const ( + testUserAgent = "user/agent" + testLatency = time.Millisecond +) + +type testHandler struct { + id string + position HandlerPosition + handleRequest func(ctx context.Context, r *http.Request) + handleResponse func(ctx context.Context, r *http.Response) + start time.Time + end time.Time + requestIDs []string + responseIDs []string + operations []string +} + +var _ RequestHandler = (*testHandler)(nil) +var _ ResponseHandler = (*testHandler)(nil) + +func (t *testHandler) ID() string { + return t.id +} + +func (t *testHandler) Position() HandlerPosition { + return t.position +} + +func (t *testHandler) HandleRequest(ctx context.Context, r *http.Request) { + t.start = time.Now() + t.requestIDs = append(t.requestIDs, GetRequestID(ctx)) + t.operations = append(t.operations, GetOperationName(ctx)) + if t.handleRequest != nil { + t.handleRequest(ctx, r) + } +} + +func (t *testHandler) HandleResponse(ctx context.Context, r *http.Response) { + t.end = time.Now() + t.responseIDs = append(t.responseIDs, GetRequestID(ctx)) + t.operations = append(t.operations, GetOperationName(ctx)) + if t.handleResponse != nil { + t.handleResponse(ctx, r) + } +} + +func (t *testHandler) Latency() time.Duration { + return t.end.Sub(t.start) +} + +type recordOrder struct { + order []string +} + +func (ro *recordOrder) Handle(id string) func(context.Context, *http.Request) { + return func(context.Context, *http.Request) { + ro.order = append(ro.order, id) + } +} + +func TestHandlerPosition(t *testing.T) { + testCases := []struct { + position HandlerPosition + str string + }{ + {position: After, str: "after"}, + {position: Before, str: "before"}, + } + for _, testCase := range testCases { + position := testCase.position + got, err := position.MarshalText() + assert.NoError(t, err) + assert.EqualValues(t, testCase.str, got) + assert.NoError(t, position.UnmarshalText(got)) + assert.Equal(t, position, testCase.position) + } +} + +func TestInvalidHandlerPosition(t *testing.T) { + position := HandlerPosition(-1) + got, err := position.MarshalText() + assert.Error(t, err) + assert.ErrorIs(t, err, errUnsupportedPosition) + assert.Nil(t, got) + err = position.UnmarshalText([]byte("HandlerPosition(-1)")) + assert.Error(t, err) + assert.ErrorIs(t, err, errUnsupportedPosition) +} + +func TestInvalidHandlers(t *testing.T) { + handler := new(MockHandler) + handler.On("ID").Return("invalid handler") + handler.On("Position").Return(HandlerPosition(-1)) + middleware := new(MockMiddlewareExtension) + middleware.On("Handlers").Return([]RequestHandler{handler}, []ResponseHandler{handler}) + c := NewConfigurer(middleware.Handlers()) + testCases := []SDKVersion{SDKv1(&request.Handlers{}), SDKv2(&awsv2.Config{})} + for _, testCase := range testCases { + err := c.Configure(testCase) + assert.Error(t, err) + assert.ErrorIs(t, err, errInvalidHandler) + assert.ErrorIs(t, err, errUnsupportedPosition) + handler.AssertNotCalled(t, "HandleRequest", mock.Anything, mock.Anything) + handler.AssertNotCalled(t, "HandleResponse", mock.Anything, mock.Anything) + } +} + +func TestConfigureUnsupported(t *testing.T) { + type unsupportedVersion struct { + SDKVersion + } + err := NewConfigurer(nil, nil).Configure(unsupportedVersion{}) + assert.Error(t, err) + assert.ErrorIs(t, err, errUnsupportedVersion) +} + +func TestAppendOrder(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + testCases := map[string]struct { + requestHandlers []*testHandler + wantOrder []string + }{ + "WithBothBefore": { + requestHandlers: []*testHandler{ + {id: "1", position: Before}, + {id: "2", position: Before}, + }, + wantOrder: []string{"2", "1"}, + }, + "WithBothAfter": { + requestHandlers: []*testHandler{ + {id: "1", position: After}, + {id: "2", position: After}, + }, + wantOrder: []string{"1", "2"}, + }, + "WithBeforeAfter": { + requestHandlers: []*testHandler{ + {id: "1", position: Before}, + {id: "2", position: After}, + }, + wantOrder: []string{"1", "2"}, + }, + "WithAfterBefore": { + requestHandlers: []*testHandler{ + {id: "1", position: After}, + {id: "2", position: Before}, + }, + wantOrder: []string{"2", "1"}, + }, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + recorder := &recordOrder{} + var requestHandlers []RequestHandler + for _, handler := range testCase.requestHandlers { + handler.handleRequest = recorder.Handle(handler.id) + requestHandlers = append(requestHandlers, handler) + } + handler := new(MockHandler) + handler.On("ID").Return("mock") + handler.On("Position").Return(After) + handler.On("HandleRequest", mock.Anything, mock.Anything) + handler.On("HandleResponse", mock.Anything, mock.Anything) + requestHandlers = append(requestHandlers, handler) + middleware := new(MockMiddlewareExtension) + middleware.On("Handlers").Return( + requestHandlers, + []ResponseHandler{handler}, + ) + c := NewConfigurer(middleware.Handlers()) + // v1 + client := awstesting.NewClient(&awsv1.Config{ + Region: awsv1.String("mock-region"), + DisableSSL: awsv1.Bool(true), + Endpoint: awsv1.String(server.URL), + }) + assert.NoError(t, c.Configure(SDKv1(&client.Handlers))) + s3v1Client := &s3v1.S3{Client: client} + _, err := s3v1Client.ListBuckets(&s3v1.ListBucketsInput{}) + require.NoError(t, err) + assert.Equal(t, testCase.wantOrder, recorder.order) + recorder.order = nil + // v2 + cfg := awsv2.Config{Region: "us-east-1"} + assert.NoError(t, c.Configure(SDKv2(&cfg))) + s3v2Client := s3v2.NewFromConfig(cfg, func(options *s3v2.Options) { + options.BaseEndpoint = awsv2.String(server.URL) + }) + _, err = s3v2Client.ListBuckets(context.Background(), &s3v2.ListBucketsInput{}) + require.NoError(t, err) + assert.Equal(t, testCase.wantOrder, recorder.order) + }) + } +} + +func TestRoundTripSDKv1(t *testing.T) { + middleware, recorder, server := setup(t) + defer server.Close() + client := awstesting.NewClient(&awsv1.Config{ + Region: awsv1.String("mock-region"), + DisableSSL: awsv1.Bool(true), + Endpoint: awsv1.String(server.URL), + MaxRetries: awsv1.Int(0), + }) + require.Equal(t, 3, client.Handlers.Build.Len()) + require.Equal(t, 1, client.Handlers.ValidateResponse.Len()) + assert.NoError(t, NewConfigurer(middleware.Handlers()).Configure(SDKv1(&client.Handlers))) + assert.Equal(t, 5, client.Handlers.Build.Len()) + assert.Equal(t, 2, client.Handlers.ValidateResponse.Len()) + s3Client := &s3v1.S3{Client: client} + output, err := s3Client.ListBuckets(&s3v1.ListBucketsInput{}) + require.NoError(t, err) + assert.NotNil(t, output) + assert.GreaterOrEqual(t, recorder.Latency(), testLatency) + assert.Equal(t, recorder.requestIDs, recorder.responseIDs) + for _, operation := range recorder.operations { + assert.Equal(t, "ListBuckets", operation) + } +} + +func TestRoundTripSDKv2(t *testing.T) { + middleware, recorder, server := setup(t) + defer server.Close() + cfg := awsv2.Config{Region: "mock-region", RetryMaxAttempts: 0} + assert.NoError(t, NewConfigurer(middleware.Handlers()).Configure(SDKv2(&cfg))) + s3Client := s3v2.NewFromConfig(cfg, func(options *s3v2.Options) { + options.BaseEndpoint = awsv2.String(server.URL) + }) + output, err := s3Client.ListBuckets(context.Background(), &s3v2.ListBucketsInput{}) + require.NoError(t, err) + assert.NotNil(t, output) + assert.GreaterOrEqual(t, recorder.Latency(), testLatency) + assert.Equal(t, recorder.requestIDs, recorder.responseIDs) + for _, operation := range recorder.operations { + assert.Equal(t, "ListBuckets", operation) + } +} + +func userAgentHandler() RequestHandler { + return &testHandler{ + id: "test.UserAgent", + position: Before, + handleRequest: func(_ context.Context, r *http.Request) { + r.Header.Set("User-Agent", testUserAgent) + }, + } +} + +func setup(t *testing.T) (Middleware, *testHandler, *httptest.Server) { + t.Helper() + recorder := &testHandler{id: "test.Latency", position: After} + middleware := new(MockMiddlewareExtension) + middleware.On("Handlers").Return( + []RequestHandler{userAgentHandler(), recorder}, + []ResponseHandler{recorder}, + ) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gotUserAgent := r.Header.Get("User-Agent") + assert.Contains(t, gotUserAgent, testUserAgent) + time.Sleep(testLatency) + w.WriteHeader(http.StatusOK) + })) + return middleware, recorder, server +} diff --git a/extension/awsmiddleware/mock.go b/extension/awsmiddleware/mock.go new file mode 100644 index 000000000000..df5fa44dfd61 --- /dev/null +++ b/extension/awsmiddleware/mock.go @@ -0,0 +1,79 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + +import ( + "context" + "net/http" + + "github.com/stretchr/testify/mock" + "go.opentelemetry.io/collector/component" +) + +// MockMiddlewareExtension mocks the Extension interface. +type MockMiddlewareExtension struct { + component.StartFunc + component.ShutdownFunc + mock.Mock +} + +var _ Extension = (*MockMiddlewareExtension)(nil) + +func (m *MockMiddlewareExtension) Handlers() ([]RequestHandler, []ResponseHandler) { + var requestHandlers []RequestHandler + var responseHandlers []ResponseHandler + args := m.Called() + arg := args.Get(0) + if arg != nil { + requestHandlers = arg.([]RequestHandler) + } + arg = args.Get(1) + if arg != nil { + responseHandlers = arg.([]ResponseHandler) + } + return requestHandlers, responseHandlers +} + +// MockHandler mocks the functions for both +// RequestHandler and ResponseHandler. +type MockHandler struct { + mock.Mock +} + +var _ RequestHandler = (*MockHandler)(nil) +var _ ResponseHandler = (*MockHandler)(nil) + +func (m *MockHandler) ID() string { + args := m.Called() + return args.String(0) +} + +func (m *MockHandler) Position() HandlerPosition { + args := m.Called() + return args.Get(0).(HandlerPosition) +} + +func (m *MockHandler) HandleRequest(ctx context.Context, r *http.Request) { + m.Called(ctx, r) +} + +func (m *MockHandler) HandleResponse(ctx context.Context, r *http.Response) { + m.Called(ctx, r) +} + +// MockExtensionsHost only mocks the GetExtensions function. +// All other functions are ignored and will panic if called. +type MockExtensionsHost struct { + component.Host + mock.Mock +} + +func (m *MockExtensionsHost) GetExtensions() map[component.ID]component.Component { + args := m.Called() + arg := args.Get(0) + if arg == nil { + return nil + } + return arg.(map[component.ID]component.Component) +} diff --git a/extension/awsmiddleware/options.go b/extension/awsmiddleware/options.go new file mode 100644 index 000000000000..66a39679f8ac --- /dev/null +++ b/extension/awsmiddleware/options.go @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + +import ( + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go/aws/request" +) + +type SDKVersion interface { + unused() +} + +type sdkVersion1 struct { + SDKVersion + handlers *request.Handlers +} + +// SDKv1 takes in AWS SDKv1 client request handlers. +func SDKv1(handlers *request.Handlers) SDKVersion { + return sdkVersion1{handlers: handlers} +} + +type sdkVersion2 struct { + SDKVersion + cfg *aws.Config +} + +// SDKv2 takes in an AWS SDKv2 config. +func SDKv2(cfg *aws.Config) SDKVersion { + return sdkVersion2{cfg: cfg} +} diff --git a/extension/awsmiddleware/wrapper.go b/extension/awsmiddleware/wrapper.go new file mode 100644 index 000000000000..be725f8ca845 --- /dev/null +++ b/extension/awsmiddleware/wrapper.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package awsmiddleware // import "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + +import ( + "context" + + sdkmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/transport/http" + "github.com/google/uuid" +) + +type ( + requestIDKey struct{} + operationNameKey struct{} +) + +func namedRequestHandler(handler RequestHandler) request.NamedHandler { + return request.NamedHandler{Name: handler.ID(), Fn: func(r *request.Request) { + ctx := mustRequestID(r.Context()) + ctx = setOperationName(ctx, r.Operation.Name) + r.SetContext(ctx) + handler.HandleRequest(ctx, r.HTTPRequest) + }} +} + +func namedResponseHandler(handler ResponseHandler) request.NamedHandler { + return request.NamedHandler{Name: handler.ID(), Fn: func(r *request.Request) { + handler.HandleResponse(r.Context(), r.HTTPResponse) + }} +} + +type requestMiddleware struct { + RequestHandler +} + +var _ middleware.BuildMiddleware = (*requestMiddleware)(nil) + +func (r requestMiddleware) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (out middleware.BuildOutput, metadata middleware.Metadata, err error) { + req, ok := in.Request.(*http.Request) + if ok { + ctx = mustRequestID(ctx) + ctx = setOperationName(ctx, sdkmiddleware.GetOperationName(ctx)) + r.HandleRequest(ctx, req.Request) + } + return next.HandleBuild(ctx, in) +} + +func withBuildOption(rmw *requestMiddleware, position middleware.RelativePosition) func(stack *middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Build.Add(rmw, position) + } +} + +type responseMiddleware struct { + ResponseHandler +} + +var _ middleware.DeserializeMiddleware = (*responseMiddleware)(nil) + +func (r responseMiddleware) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (out middleware.DeserializeOutput, metadata middleware.Metadata, err error) { + out, metadata, err = next.HandleDeserialize(ctx, in) + res, ok := out.RawResponse.(*http.Response) + if ok { + r.HandleResponse(ctx, res.Response) + } + return +} + +func withDeserializeOption(rmw *responseMiddleware, position middleware.RelativePosition) func(stack *middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Deserialize.Add(rmw, position) + } +} + +func mustRequestID(ctx context.Context) context.Context { + requestID := GetRequestID(ctx) + if requestID != "" { + return ctx + } + return setRequestID(ctx, uuid.NewString()) +} + +func setRequestID(ctx context.Context, id string) context.Context { + return context.WithValue(ctx, requestIDKey{}, id) +} + +func setOperationName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, operationNameKey{}, name) +} + +// GetRequestID retrieves the generated request ID from the context. +func GetRequestID(ctx context.Context) string { + requestID, _ := ctx.Value(requestIDKey{}).(string) + return requestID +} + +// GetOperationName retrieves the service operation metadata from the context. +func GetOperationName(ctx context.Context) string { + operationName, _ := ctx.Value(operationNameKey{}).(string) + return operationName +} diff --git a/go.mod b/go.mod index cd35399db478..b9e88b85cf57 100644 --- a/go.mod +++ b/go.mod @@ -265,6 +265,7 @@ require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/aliyun/aliyun-log-go-sdk v0.1.54 // indirect + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20231023152757-c6e2437e6590 // indirect github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20230818193829-04a761abd409 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/antonmedv/expr v1.15.0 // indirect @@ -274,8 +275,8 @@ require ( github.com/apache/thrift v0.19.0 // indirect github.com/ardielle/ardielle-go v1.5.2 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.45.2 // indirect - github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect + github.com/aws/aws-sdk-go v1.45.24 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.38 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.36 // indirect @@ -284,17 +285,17 @@ require ( github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect - github.com/aws/smithy-go v1.14.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect @@ -827,6 +828,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/zipki replace github.com/open-telemetry/opentelemetry-collector-contrib/extension/asapauthextension => ./extension/asapauthextension +replace github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => ./extension/awsmiddleware + replace github.com/open-telemetry/opentelemetry-collector-contrib/extension/awsproxy => ./extension/awsproxy replace github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension => ./extension/basicauthextension diff --git a/go.sum b/go.sum index 475a29ad4b8f..56622ce6b2b8 100644 --- a/go.sum +++ b/go.sum @@ -946,14 +946,15 @@ github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9 github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk= -github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.24 h1:TZx/CizkmCQn8Rtsb11iLYutEQVGK5PK9wAhwouELBo= +github.com/aws/aws-sdk-go v1.45.24/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= @@ -981,24 +982,29 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8= github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5 h1:naSZmQiFjoTLxNjfDy/KgEnWdG3odkR6gIEgTx21YOM= github.com/aws/aws-sdk-go-v2/service/kinesis v1.18.5/go.mod h1:0h3hOcyFXyjvI3wGt8C8vk2+II9XxHwFM7zH2KvLHmA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= @@ -1012,8 +1018,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvT github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw= github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= diff --git a/internal/aws/containerinsight/const.go b/internal/aws/containerinsight/const.go index d6ff6ecdbc9c..38a94360bd8b 100644 --- a/internal/aws/containerinsight/const.go +++ b/internal/aws/containerinsight/const.go @@ -270,12 +270,14 @@ func init() { StatusContainerWaitingReasonStartError: UnitCount, StatusContainerWaitingReasonCreateContainerConfigError: UnitCount, StatusContainerWaitingReasonCreateContainerError: UnitCount, - StatusFailed: UnitCount, - StatusPending: UnitCount, - StatusSucceeded: UnitCount, - StatusUnknown: UnitCount, - StatusReady: UnitCount, - StatusScheduled: UnitCount, + StatusContainerTerminatedReasonOOMKilled: UnitCount, + StatusRunning: UnitCount, + StatusFailed: UnitCount, + StatusPending: UnitCount, + StatusSucceeded: UnitCount, + StatusUnknown: UnitCount, + StatusReady: UnitCount, + StatusScheduled: UnitCount, // cluster metrics NodeCount: UnitCount, diff --git a/internal/aws/cwlogs/cwlog_client.go b/internal/aws/cwlogs/cwlog_client.go index 7e36fc5f690e..e949c83650ed 100644 --- a/internal/aws/cwlogs/cwlog_client.go +++ b/internal/aws/cwlogs/cwlog_client.go @@ -43,7 +43,7 @@ type UserAgentOption func(*UserAgentFlag) type UserAgentFlag struct { isEnhancedContainerInsights bool - isPulseApm bool + isAppSignals bool } func WithEnabledContainerInsights(flag bool) UserAgentOption { @@ -52,9 +52,9 @@ func WithEnabledContainerInsights(flag bool) UserAgentOption { } } -func WithEnabledPulseApm(flag bool) UserAgentOption { +func WithEnabledAppSignals(flag bool) UserAgentOption { return func(ua *UserAgentFlag) { - ua.isPulseApm = flag + ua.isAppSignals = flag } } @@ -76,7 +76,7 @@ func NewClient(logger *zap.Logger, awsConfig *aws.Config, buildInfo component.Bu // Loop through each option option := &UserAgentFlag{ isEnhancedContainerInsights: false, - isPulseApm: false, + isAppSignals: false, } for _, opt := range opts { opt(option) @@ -86,6 +86,10 @@ func NewClient(logger *zap.Logger, awsConfig *aws.Config, buildInfo component.Bu return newCloudWatchLogClient(client, logRetention, tags, logger) } +func (client *Client) Handlers() *request.Handlers { + return &client.svc.(*cloudwatchlogs.CloudWatchLogs).Handlers +} + // PutLogEvents mainly handles different possible error could be returned from server side, and retries them // if necessary. func (client *Client) PutLogEvents(input *cloudwatchlogs.PutLogEventsInput, retryCnt int) (*string, error) { @@ -225,8 +229,8 @@ func newCollectorUserAgentHandler(buildInfo component.BuildInfo, logGroupName st extraStr = "EnhancedEKSContainerInsights" case containerInsightsRegexPattern.MatchString(logGroupName): extraStr = "ContainerInsights" - case userAgentFlag.isPulseApm: - extraStr = "Pulse" + case userAgentFlag.isAppSignals: + extraStr = "AppSignals" } fn := request.MakeAddToUserAgentHandler(buildInfo.Command, buildInfo.Version, extraStr) diff --git a/internal/aws/cwlogs/cwlog_client_test.go b/internal/aws/cwlogs/cwlog_client_test.go index 5a2f6594d1e8..aef673beb6a0 100644 --- a/internal/aws/cwlogs/cwlog_client_test.go +++ b/internal/aws/cwlogs/cwlog_client_test.go @@ -596,10 +596,10 @@ func TestUserAgent(t *testing.T) { "opentelemetry-collector-contrib/1.0", }, { - "emptyLogGroupPulse", + "emptyLogGroupAppSignals", component.BuildInfo{Command: "opentelemetry-collector-contrib", Version: "1.0"}, "", - WithEnabledPulseApm(false), + WithEnabledAppSignals(false), "opentelemetry-collector-contrib/1.0", }, { @@ -610,10 +610,10 @@ func TestUserAgent(t *testing.T) { "test-collector-contrib/1.0", }, { - "buildInfoCommandUsedPulse", + "buildInfoCommandUsedAppSignals", component.BuildInfo{Command: "test-collector-contrib", Version: "1.0"}, "", - WithEnabledPulseApm(false), + WithEnabledAppSignals(false), "test-collector-contrib/1.0", }, { @@ -660,17 +660,17 @@ func TestUserAgent(t *testing.T) { "opentelemetry-collector-contrib/1.0 (ContainerInsights)", }, { - "validPulseEMFEnabled", + "validAppSignalsEMFEnabled", component.BuildInfo{Command: "opentelemetry-collector-contrib", Version: "1.0"}, - "/aws/apm", - WithEnabledPulseApm(true), - "opentelemetry-collector-contrib/1.0 (Pulse)", + "/aws/appsignals", + WithEnabledAppSignals(true), + "opentelemetry-collector-contrib/1.0 (AppSignals)", }, { - "PulseEMFNotEnabled", + "AppSignalsEMFNotEnabled", component.BuildInfo{Command: "opentelemetry-collector-contrib", Version: "1.0"}, - "/aws/apm", - WithEnabledPulseApm(false), + "/aws/appsignals", + WithEnabledAppSignals(false), "opentelemetry-collector-contrib/1.0", }, } @@ -679,14 +679,13 @@ func TestUserAgent(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { cwlog := NewClient(logger, &aws.Config{}, tc.buildInfo, tc.logGroupName, 0, map[string]*string{}, session, tc.userAgentOption) - logClient := cwlog.svc.(*cloudwatchlogs.CloudWatchLogs) - req := request.New(aws.Config{}, metadata.ClientInfo{}, logClient.Handlers, nil, &request.Operation{ + req := request.New(aws.Config{}, metadata.ClientInfo{}, *cwlog.Handlers(), nil, &request.Operation{ HTTPMethod: "GET", HTTPPath: "/", }, nil, nil) - logClient.Handlers.Build.Run(req) + cwlog.Handlers().Build.Run(req) assert.Contains(t, req.HTTPRequest.UserAgent(), tc.expectedUserAgentStr) }) } diff --git a/internal/aws/xray/telemetry/sender_test.go b/internal/aws/xray/telemetry/sender_test.go index 0b0ceff32936..c32d64ec82e6 100644 --- a/internal/aws/xray/telemetry/sender_test.go +++ b/internal/aws/xray/telemetry/sender_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/aws/aws-sdk-go/aws/request" awsmock "github.com/aws/aws-sdk-go/awstesting/mock" "github.com/aws/aws-sdk-go/service/xray" "github.com/stretchr/testify/assert" @@ -39,6 +40,14 @@ func (m *mockClient) PutTelemetryRecords(input *xray.PutTelemetryRecordsInput) ( return args.Get(0).(*xray.PutTelemetryRecordsOutput), args.Error(1) } +func (m *mockClient) Handlers() *request.Handlers { + args := m.Called() + if args.Get(0) == nil { + return nil + } + return args.Get(0).(*request.Handlers) +} + func TestRotateRace(t *testing.T) { client := &mockClient{count: &atomic.Int64{}} client.On("PutTelemetryRecords", mock.Anything).Return(nil, nil).Once() diff --git a/internal/aws/xray/xray_client.go b/internal/aws/xray/xray_client.go index adb8d0ba7262..49f092b23d58 100644 --- a/internal/aws/xray/xray_client.go +++ b/internal/aws/xray/xray_client.go @@ -30,12 +30,18 @@ type XRayClient interface { PutTraceSegments(input *xray.PutTraceSegmentsInput) (*xray.PutTraceSegmentsOutput, error) // PutTelemetryRecords makes PutTelemetryRecords api call on X-Ray client. PutTelemetryRecords(input *xray.PutTelemetryRecordsInput) (*xray.PutTelemetryRecordsOutput, error) + // Handlers exposes the X-Ray client handlers. + Handlers() *request.Handlers } type xrayClient struct { xRay *xray.XRay } +func (c *xrayClient) Handlers() *request.Handlers { + return &c.xRay.Handlers +} + // PutTraceSegments makes PutTraceSegments api call on X-Ray client. func (c *xrayClient) PutTraceSegments(input *xray.PutTraceSegmentsInput) (*xray.PutTraceSegmentsOutput, error) { return c.xRay.PutTraceSegments(input) diff --git a/internal/aws/xray/xray_client_test.go b/internal/aws/xray/xray_client_test.go index 9fba5394434a..5c158f11316b 100644 --- a/internal/aws/xray/xray_client_test.go +++ b/internal/aws/xray/xray_client_test.go @@ -27,14 +27,13 @@ func TestUserAgent(t *testing.T) { newSession, err := session.NewSession() require.NoError(t, err) xray := NewXRayClient(logger, &aws.Config{}, buildInfo, newSession).(*xrayClient) - x := xray.xRay - req := request.New(aws.Config{}, metadata.ClientInfo{}, x.Handlers, nil, &request.Operation{ + req := request.New(aws.Config{}, metadata.ClientInfo{}, *xray.Handlers(), nil, &request.Operation{ HTTPMethod: "GET", HTTPPath: "/", }, nil, nil) - x.Handlers.Build.Run(req) + xray.Handlers().Build.Run(req) assert.Contains(t, req.HTTPRequest.UserAgent(), "test-collector-contrib/1.0") assert.Contains(t, req.HTTPRequest.UserAgent(), "xray-otel-exporter/") assert.Contains(t, req.HTTPRequest.UserAgent(), "exec-env/") diff --git a/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper.go b/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper.go index 83c3500cc9c7..483a9357388d 100644 --- a/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper.go +++ b/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper.go @@ -28,6 +28,8 @@ import ( const ( caFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" collectionInterval = 60 * time.Second + // needs to start with "containerInsightsKubeAPIServerScraper" for histogram deltas in the emf exporter + jobName = "containerInsightsKubeAPIServerScraper" ) var ( @@ -45,9 +47,9 @@ var ( "^apiserver_requested_deprecated_apis$", "^apiserver_storage_list_duration_seconds_(bucket|sum|count)$", "^apiserver_storage_objects$", - "^apiserver_storage_db_total_size_in_bytes_(bucket|sum|count)$", - "^apiserver_storage_size_bytes_(bucket|sum|count)$", - "^etcd_db_total_size_in_bytes_(bucket|sum|count)$", + "^apiserver_storage_db_total_size_in_bytes$", + "^apiserver_storage_size_bytes$", + "^etcd_db_total_size_in_bytes$", "^etcd_request_duration_seconds_(bucket|sum|count)$", "^rest_client_request_duration_seconds_(bucket|sum|count)$", "^rest_client_requests_total$", @@ -104,7 +106,7 @@ func NewPrometheusScraper(opts PrometheusScraperOpts) (*PrometheusScraper, error }, ScrapeInterval: model.Duration(collectionInterval), ScrapeTimeout: model.Duration(collectionInterval), - JobName: fmt.Sprintf("%s/%s", "containerInsightsKubeAPIServerScraper", opts.Endpoint), + JobName: fmt.Sprintf("%s/%s", jobName, opts.Endpoint), HonorTimestamps: true, Scheme: "https", MetricsPath: "/metrics", diff --git a/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper_test.go b/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper_test.go index 540ceb976db8..33dc4ba57071 100644 --- a/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper_test.go +++ b/receiver/awscontainerinsightreceiver/internal/k8sapiserver/prometheus_scraper_test.go @@ -198,7 +198,7 @@ func TestNewPrometheusScraperEndToEnd(t *testing.T) { }, ScrapeInterval: cfg.ScrapeConfigs[0].ScrapeInterval, ScrapeTimeout: cfg.ScrapeConfigs[0].ScrapeInterval, - JobName: fmt.Sprintf("%s/%s", "containerInsightsKubeAPIServerScraper", cfg.ScrapeConfigs[0].MetricsPath), + JobName: fmt.Sprintf("%s/%s", jobName, cfg.ScrapeConfigs[0].MetricsPath), HonorTimestamps: true, Scheme: "http", MetricsPath: cfg.ScrapeConfigs[0].MetricsPath, @@ -269,3 +269,8 @@ func TestNewPrometheusScraperEndToEnd(t *testing.T) { assert.True(t, *consumer.relabeled) assert.False(t, *consumer.rpcDurationTotal) // this will get filtered out by our metric relabel config } + +func TestPrometheusScraperJobName(t *testing.T) { + // needs to start with containerInsights + assert.True(t, strings.HasPrefix(jobName, "containerInsightsKubeAPIServerScraper")) +} diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podstore.go index a14817a8c2aa..a90b2a70e207 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podstore.go @@ -608,11 +608,7 @@ func (p *PodStore) addPodContainerStatusMetrics(metric CIMetric, pod *corev1.Pod reason := containerStatus.State.Waiting.Reason if reason != "" { if val, ok := ci.WaitingReasonLookup[reason]; ok { - if _, foundStatus := possibleStatuses[val]; foundStatus { - possibleStatuses[val]++ - } else { - possibleStatuses[val] = 1 - } + possibleStatuses[val]++ } } case containerStatus.State.Terminated != nil: @@ -624,11 +620,7 @@ func (p *PodStore) addPodContainerStatusMetrics(metric CIMetric, pod *corev1.Pod if containerStatus.LastTerminationState.Terminated != nil && containerStatus.LastTerminationState.Terminated.Reason != "" { if strings.Contains(containerStatus.LastTerminationState.Terminated.Reason, "OOMKilled") { - if _, foundStatus := possibleStatuses[ci.StatusContainerTerminatedReasonOOMKilled]; foundStatus { - possibleStatuses[ci.StatusContainerTerminatedReasonOOMKilled]++ - } else { - possibleStatuses[ci.StatusContainerTerminatedReasonOOMKilled] = 1 - } + possibleStatuses[ci.StatusContainerTerminatedReasonOOMKilled]++ } } } diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podstore_test.go index 367ee59f8dbf..dff67bfe8dca 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podstore_test.go @@ -720,7 +720,6 @@ func TestPodStore_addStatus_enhanced_metrics(t *testing.T) { metric = generateMetric(fields, tags) podStore.addStatus(metric, pod) - //assert.Equal(t, "Waiting", metric.GetTag(ci.ContainerStatus)) assert.Equal(t, 2, metric.GetField(ci.MetricName(ci.TypePod, ci.StatusContainerWaiting))) assert.Equal(t, 2, metric.GetField(ci.MetricName(ci.TypePod, ci.StatusContainerWaitingReasonCrashLoopBackOff))) // sparse metrics