diff --git a/.github/actions/setup-go-tip/action.yml b/.github/actions/setup-go-tip/action.yml index 5b56d1ff801..4d0be775881 100644 --- a/.github/actions/setup-go-tip/action.yml +++ b/.github/actions/setup-go-tip/action.yml @@ -43,7 +43,7 @@ runs: # This requires Go toolchain, so install it first. - name: Install Go toolchain if: steps.download.outputs.success == 'false' - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/actions/setup-node.js/action.yml b/.github/actions/setup-node.js/action.yml index 21344f6c734..389604490ac 100644 --- a/.github/actions/setup-node.js/action.yml +++ b/.github/actions/setup-node.js/action.yml @@ -8,7 +8,7 @@ runs: run: | echo "JAEGER_UI_NODE_JS_VERSION=$(cat jaeger-ui/.nvmrc)" >> ${GITHUB_ENV} - - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: ${{ env.JAEGER_UI_NODE_JS_VERSION }} cache: 'yarn' diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 161bcb1159e..3678946f321 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,12 +25,6 @@ updates: interval: daily labels: [ "changelog:dependencies" ] - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - labels: [ "changelog:dependencies" ] - - package-ecosystem: docker directories: - /cmd/agent diff --git a/.github/workflows/ci-build-binaries.yml b/.github/workflows/ci-build-binaries.yml index 56255ed8b17..3d2fa327fd6 100644 --- a/.github/workflows/ci-build-binaries.yml +++ b/.github/workflows/ci-build-binaries.yml @@ -50,7 +50,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-crossdock.yml b/.github/workflows/ci-crossdock.yml index 584bf1d9d8e..23938fb4802 100644 --- a/.github/workflows/ci-crossdock.yml +++ b/.github/workflows/ci-crossdock.yml @@ -33,7 +33,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x @@ -43,7 +43,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Build, test, and publish crossdock image run: bash scripts/build-crossdock.sh diff --git a/.github/workflows/ci-all-in-one-build.yml b/.github/workflows/ci-docker-all-in-one.yml similarity index 92% rename from .github/workflows/ci-all-in-one-build.yml rename to .github/workflows/ci-docker-all-in-one.yml index beec87f5254..9cc91cb8fc2 100644 --- a/.github/workflows/ci-all-in-one-build.yml +++ b/.github/workflows/ci-docker-all-in-one.yml @@ -42,7 +42,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x @@ -55,7 +55,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Define BUILD_FLAGS var if running on a Pull Request run: | diff --git a/.github/workflows/ci-docker-build.yml b/.github/workflows/ci-docker-build.yml index 9f0afa50b43..227ee0aff02 100644 --- a/.github/workflows/ci-docker-build.yml +++ b/.github/workflows/ci-docker-build.yml @@ -33,7 +33,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x @@ -46,7 +46,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Build only linux/amd64 container images for a Pull Request if: github.ref_name != 'main' diff --git a/.github/workflows/ci-hotrod.yml b/.github/workflows/ci-docker-hotrod.yml similarity index 84% rename from .github/workflows/ci-hotrod.yml rename to .github/workflows/ci-docker-hotrod.yml index 98060ffe53d..1bba419d9a9 100644 --- a/.github/workflows/ci-hotrod.yml +++ b/.github/workflows/ci-docker-hotrod.yml @@ -32,7 +32,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x @@ -42,7 +42,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Build, test, and publish hotrod image run: bash scripts/hotrod-integration-test.sh @@ -51,5 +51,5 @@ jobs: QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} - name: Print logs from hotrod - run: docker logs example-hotrod + run: docker compose -f ./examples/hotrod/docker-compose.yml logs if: failure() diff --git a/.github/workflows/ci-badger.yaml b/.github/workflows/ci-e2e-badger.yaml similarity index 94% rename from .github/workflows/ci-badger.yaml rename to .github/workflows/ci-e2e-badger.yaml index 82d23b76621..e91e031409d 100644 --- a/.github/workflows/ci-badger.yaml +++ b/.github/workflows/ci-e2e-badger.yaml @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-cassandra.yml b/.github/workflows/ci-e2e-cassandra.yml similarity index 95% rename from .github/workflows/ci-cassandra.yml rename to .github/workflows/ci-e2e-cassandra.yml index 97501f55a7f..f5455d52113 100644 --- a/.github/workflows/ci-cassandra.yml +++ b/.github/workflows/ci-e2e-cassandra.yml @@ -37,7 +37,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-elasticsearch.yml b/.github/workflows/ci-e2e-elasticsearch.yml similarity index 89% rename from .github/workflows/ci-elasticsearch.yml rename to .github/workflows/ci-e2e-elasticsearch.yml index 108bea334db..c80dc0f8686 100644 --- a/.github/workflows/ci-elasticsearch.yml +++ b/.github/workflows/ci-e2e-elasticsearch.yml @@ -22,6 +22,9 @@ jobs: fail-fast: false matrix: version: + - major: 6.x + distribution: elasticsearch + jaeger: v1 - major: 7.x distribution: elasticsearch jaeger: v1 @@ -46,14 +49,11 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x - - name: Install tools - run: make install-ci - - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Run ${{ matrix.version.distribution }} integration tests id: test-execution run: bash scripts/es-integration-test.sh ${{ matrix.version.distribution }} ${{ matrix.version.major }} ${{ matrix.version.jaeger }} diff --git a/.github/workflows/ci-grpc.yml b/.github/workflows/ci-e2e-grpc.yml similarity index 94% rename from .github/workflows/ci-grpc.yml rename to .github/workflows/ci-e2e-grpc.yml index d21c59e7fdf..a366488f267 100644 --- a/.github/workflows/ci-grpc.yml +++ b/.github/workflows/ci-e2e-grpc.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-kafka.yml b/.github/workflows/ci-e2e-kafka.yml similarity index 94% rename from .github/workflows/ci-kafka.yml rename to .github/workflows/ci-e2e-kafka.yml index 5d570e3aa4c..eb2530dc210 100644 --- a/.github/workflows/ci-kafka.yml +++ b/.github/workflows/ci-e2e-kafka.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-e2e-memory.yaml b/.github/workflows/ci-e2e-memory.yaml new file mode 100644 index 00000000000..3a8dce7d370 --- /dev/null +++ b/.github/workflows/ci-e2e-memory.yaml @@ -0,0 +1,41 @@ +name: CIT Memory + +on: + push: + branches: [main] + + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ (github.event.pull_request && github.event.pull_request.number) || github.ref || github.run_id }} + cancel-in-progress: true + +# See https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +jobs: + memory-v2: + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: 1.22.x + + - name: Run Memory storage integration tests + run: | + STORAGE=memory_v2 make jaeger-v2-storage-integration-test + + - name: Upload coverage to codecov + uses: ./.github/actions/upload-codecov + with: + files: cover.out + flags: memory_v2 diff --git a/.github/workflows/ci-opensearch.yml b/.github/workflows/ci-e2e-opensearch.yml similarity index 90% rename from .github/workflows/ci-opensearch.yml rename to .github/workflows/ci-e2e-opensearch.yml index c9185f6d0e2..50ee0586386 100644 --- a/.github/workflows/ci-opensearch.yml +++ b/.github/workflows/ci-e2e-opensearch.yml @@ -46,14 +46,11 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x - - name: Install tools - run: make install-ci - - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Run ${{ matrix.version.distribution }} integration tests id: test-execution diff --git a/.github/workflows/ci-build-spm.yml b/.github/workflows/ci-e2e-spm.yml similarity index 93% rename from .github/workflows/ci-build-spm.yml rename to .github/workflows/ci-e2e-spm.yml index 54095cb187f..8f58de52f91 100644 --- a/.github/workflows/ci-build-spm.yml +++ b/.github/workflows/ci-e2e-spm.yml @@ -32,7 +32,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-lint-checks.yaml b/.github/workflows/ci-lint-checks.yaml index 41c30b023ed..7233689a50c 100644 --- a/.github/workflows/ci-lint-checks.yaml +++ b/.github/workflows/ci-lint-checks.yaml @@ -39,7 +39,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/validate-dependabot-config.yml b/.github/workflows/ci-lint-dependabot-config.yml similarity index 100% rename from .github/workflows/validate-dependabot-config.yml rename to .github/workflows/ci-lint-dependabot-config.yml diff --git a/.github/workflows/ci-protogen-tests.yml b/.github/workflows/ci-lint-protogen.yml similarity index 94% rename from .github/workflows/ci-protogen-tests.yml rename to .github/workflows/ci-lint-protogen.yml index 8aec34caa1c..99cde657896 100644 --- a/.github/workflows/ci-protogen-tests.yml +++ b/.github/workflows/ci-lint-protogen.yml @@ -28,7 +28,7 @@ jobs: with: submodules: recursive - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-validation-of-shell-scripts.yml b/.github/workflows/ci-lint-shell-scripts.yml similarity index 84% rename from .github/workflows/ci-validation-of-shell-scripts.yml rename to .github/workflows/ci-lint-shell-scripts.yml index 8cc195e3102..dfa8af30633 100644 --- a/.github/workflows/ci-validation-of-shell-scripts.yml +++ b/.github/workflows/ci-lint-shell-scripts.yml @@ -1,4 +1,4 @@ -name: Validation Of Shell Scripts +name: Lint Shell Scripts on: push: @@ -11,7 +11,7 @@ permissions: contents: read jobs: - validation-of-shell-scripts: + link-shell-scripts: runs-on: ubuntu-latest steps: @@ -20,7 +20,7 @@ jobs: with: egress-policy: audit - - name: check out code + - name: check out code uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Install shellcheck @@ -28,9 +28,3 @@ jobs: - name: Run shellcheck run: shellcheck scripts/*.sh - - - - - - diff --git a/.github/workflows/ci-release-testing.yml b/.github/workflows/ci-release-testing.yml deleted file mode 100644 index 79301cdc312..00000000000 --- a/.github/workflows/ci-release-testing.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Publish release - -on: - # allow running release workflow manually - workflow_dispatch: - -# See https://github.com/jaegertracing/jaeger/issues/4017 -permissions: - contents: read - -jobs: - publish-release: - permissions: - contents: write - - runs-on: ubuntu-latest - - steps: - - - name: Disk size - run: df -g / - - - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 - with: - egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: true - - - name: Fetch git tags - run: | - git fetch --prune --unshallow --tags - - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 - with: - go-version: 1.22.x - - - name: Setup Node.js version - uses: ./.github/actions/setup-node.js - - - name: Export BRANCH variable and validate it is a semver - # Many scripts depend on BRANCH variable. We do not want to - # use ./.github/actions/setup-branch here because it may set - # BRANCH=main when the workflow is triggered manually. - run: | - BRANCH=$(make echo-version) - echo "BRANCH=${BRANCH}" >> ${GITHUB_ENV} - echo Validate that the latest tag ${BRANCH} is in semver format - echo ${BRANCH} | grep -E '^v[0-9]+.[0-9]+.[0-9]+$' - - - name: Install tools - run: make install-ci - - - name: Configure GPG Key - id: import_gpg - uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.GPG_PASSPHRASE }} - - - name: Build binaries - run: make build-all-platforms - - - name: Package binaries - id: package-binaries - run: bash scripts/package-deploy.sh - - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 - - - name: Build all container images (do not push) - run: bash scripts/build-upload-docker-images.sh -l - env: - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} - - - name: Build and test all-in-one image (do not push) - run: bash scripts/build-all-in-one-image.sh -l - env: - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} - - - name: Build, test, and publish hotrod image - run: bash scripts/hotrod-integration-test.sh - env: - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} - - - name: Generate SBOM - uses: anchore/sbom-action@e8d2a6937ecead383dfe75190d104edd1f9c5751 # v0.16.0 - with: - output-file: jaeger-SBOM.spdx.json - upload-release-assets: false - upload-artifact: false diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 537d4389a51..448637c6146 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -42,7 +42,7 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x @@ -91,7 +91,7 @@ jobs: rm -rf deploy || true df -h / - - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Build and upload all container images run: bash scripts/build-upload-docker-images.sh @@ -112,7 +112,7 @@ jobs: QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} - name: Generate SBOM - uses: anchore/sbom-action@e8d2a6937ecead383dfe75190d104edd1f9c5751 # v0.16.0 + uses: anchore/sbom-action@d94f46e13c6c62f59525ac9a1e147a99dc0b9bf5 # v0.17.0 with: output-file: jaeger-SBOM.spdx.json upload-release-assets: false diff --git a/.github/workflows/ci-unit-tests.yml b/.github/workflows/ci-unit-tests.yml index 8863da6c7e2..daa4e4840cf 100644 --- a/.github/workflows/ci-unit-tests.yml +++ b/.github/workflows/ci-unit-tests.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x cache-dependency-path: ./go.sum diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 3be346b137c..d69d008d23f 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x diff --git a/.github/workflows/ci-label-check.yml b/.github/workflows/label-check.yml similarity index 100% rename from .github/workflows/ci-label-check.yml rename to .github/workflows/label-check.yml diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d9afd92f2de..611e740f498 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -64,7 +64,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 with: name: SARIF file path: results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4949327a1..9e42ec99dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,66 @@ run `make changelog` to generate content +1.59.0 (2024-07-10) +------------------- + +### Backend Changes + +#### ⛔ Breaking Changes + +* Update opentelemetry-go to v1.28.0 and refactor references to semantic conventions ([@renovate-bot](https://github.com/renovate-bot) in [#5698](https://github.com/jaegertracing/jaeger/pull/5698)) + +#### ✨ New Features + +* Run jaeger-es-index-cleaner and jaeger-es-rollover locally ([@hellspawn679](https://github.com/hellspawn679) in [#5714](https://github.com/jaegertracing/jaeger/pull/5714)) +* [tracegen] allow use of adaptive sampling ([@yurishkuro](https://github.com/yurishkuro) in [#5718](https://github.com/jaegertracing/jaeger/pull/5718)) +* [v2] add v1 factory converter to v2 storage factory ([@james-ryans](https://github.com/james-ryans) in [#5497](https://github.com/jaegertracing/jaeger/pull/5497)) +* Upgrade badger v3->badger v4 ([@hellspawn679](https://github.com/hellspawn679) in [#5619](https://github.com/jaegertracing/jaeger/pull/5619)) + +#### 🐞 Bug fixes, Minor Improvements + +* Cleanup the prometheus config ([@FlamingSaint](https://github.com/FlamingSaint) in [#5720](https://github.com/jaegertracing/jaeger/pull/5720)) +* Upgrade microsim to v0.4.1 ([@FlamingSaint](https://github.com/FlamingSaint) in [#5702](https://github.com/jaegertracing/jaeger/pull/5702)) +* Add all mocks to mockery config file and regenerate ([@danish9039](https://github.com/danish9039) in [#5626](https://github.com/jaegertracing/jaeger/pull/5626)) +* Add better logging options ([@yurishkuro](https://github.com/yurishkuro) in [#5675](https://github.com/jaegertracing/jaeger/pull/5675)) +* Restore "operation" name in the metrics response ([@yurishkuro](https://github.com/yurishkuro) in [#5673](https://github.com/jaegertracing/jaeger/pull/5673)) +* Add flag for custom authenticators in cassandra storage ([@hellspawn679](https://github.com/hellspawn679) in [#5628](https://github.com/jaegertracing/jaeger/pull/5628)) +* Rename strategy store to sampling strategy provider ([@yurishkuro](https://github.com/yurishkuro) in [#5634](https://github.com/jaegertracing/jaeger/pull/5634)) +* [query] avoid errors when closing shared listener ([@vermaaatul07](https://github.com/vermaaatul07) in [#5559](https://github.com/jaegertracing/jaeger/pull/5559)) +* Bump github.com/golangci/golangci-lint from 1.55.2 to 1.59.1 and fix linter errors ([@FlamingSaint](https://github.com/FlamingSaint) in [#5579](https://github.com/jaegertracing/jaeger/pull/5579)) +* Fix binary path in package-deploy.sh ([@yurishkuro](https://github.com/yurishkuro) in [#5561](https://github.com/jaegertracing/jaeger/pull/5561)) + +#### 🚧 Experimental Features + +* Implement telemetery struct for v1 components initialization ([@Wise-Wizard](https://github.com/Wise-Wizard) in [#5695](https://github.com/jaegertracing/jaeger/pull/5695)) +* Support default configs for storage backends ([@yurishkuro](https://github.com/yurishkuro) in [#5691](https://github.com/jaegertracing/jaeger/pull/5691)) +* Simplify configs organization ([@yurishkuro](https://github.com/yurishkuro) in [#5690](https://github.com/jaegertracing/jaeger/pull/5690)) +* Create metrics.factory adapter for otel metrics ([@Wise-Wizard](https://github.com/Wise-Wizard) in [#5661](https://github.com/jaegertracing/jaeger/pull/5661)) + +#### 👷 CI Improvements + +* Apply 'latest' tag to latest published snapshot images ([@yurishkuro](https://github.com/yurishkuro) in [#5724](https://github.com/jaegertracing/jaeger/pull/5724)) +* [bug] use correct argument as jaeger-version ([@yurishkuro](https://github.com/yurishkuro) in [#5716](https://github.com/jaegertracing/jaeger/pull/5716)) +* Add spm integration tests ([@hellspawn679](https://github.com/hellspawn679) in [#5640](https://github.com/jaegertracing/jaeger/pull/5640)) +* Add spm build to ci ([@yurishkuro](https://github.com/yurishkuro) in [#5663](https://github.com/jaegertracing/jaeger/pull/5663)) +* Remove unnecessary .nocover files ([@yurishkuro](https://github.com/yurishkuro) in [#5642](https://github.com/jaegertracing/jaeger/pull/5642)) +* Add tests for anonymizer/app/query. ([@shanukun](https://github.com/shanukun) in [#5638](https://github.com/jaegertracing/jaeger/pull/5638)) +* Add alternate way to install gotip ([@EraKin575](https://github.com/EraKin575) in [#5618](https://github.com/jaegertracing/jaeger/pull/5618)) +* Add semver to dependencies ([@danish9039](https://github.com/danish9039) in [#5590](https://github.com/jaegertracing/jaeger/pull/5590)) +* Create config file for mockery instead of using explicit cli flags in the makefile ([@jesslourenco](https://github.com/jesslourenco) in [#5623](https://github.com/jaegertracing/jaeger/pull/5623)) +* Update renovate bot to not apply patches to e2e test dependencies ([@DustyMMiller](https://github.com/DustyMMiller) in [#5622](https://github.com/jaegertracing/jaeger/pull/5622)) +* Require renovate bot to run go mod tidy ([@yurishkuro](https://github.com/yurishkuro) in [#5612](https://github.com/jaegertracing/jaeger/pull/5612)) +* Fix new warnings from the linter upgrade ([@WaterLemons2k](https://github.com/WaterLemons2k) in [#5589](https://github.com/jaegertracing/jaeger/pull/5589)) +* [ci] validate that generated mocks are up to date ([@yurishkuro](https://github.com/yurishkuro) in [#5568](https://github.com/jaegertracing/jaeger/pull/5568)) + +### 📊 UI Changes + +#### 🐞 Bug fixes, Minor Improvements + +* Add escaped example to tag search help popup ([@yurishkuro](https://github.com/yurishkuro) in [#2354](https://github.com/jaegertracing/jaeger-ui/pull/2354)) + + + 1.58.1 (2024-06-22) ------------------- diff --git a/RELEASE.md b/RELEASE.md index 16055c03d93..559a836b7f7 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -60,8 +60,8 @@ Here are the release managers for future versions with the tentative release dat | Version | Release Manager | Tentative release date | |---------|-----------------|------------------------| -| 1.59.0 | @joe-elliott | 3 July 2024 | | 1.60.0 | @jkowall | 7 August 2024 | | 1.61.0 | @yurishkuro | 3 September 2024 | | 1.62.0 | @albertteoh | 2 October 2024 | | 1.63.0 | @pavolloffay | 5 November 2024 | +| 1.64.0 | @joe-elliott | 4 December 2024 | diff --git a/cmd/all-in-one/main.go b/cmd/all-in-one/main.go index 185c74a74ab..a051573e5a6 100644 --- a/cmd/all-in-one/main.go +++ b/cmd/all-in-one/main.go @@ -43,6 +43,7 @@ import ( "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/jtracer" "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/pkg/version" metricsPlugin "github.com/jaegertracing/jaeger/plugin/metrics" @@ -193,12 +194,17 @@ by default uses only in-memory database.`, logger.Fatal("Could not create collector proxy", zap.Error(err)) } agent := startAgent(cp, aOpts, logger, agentMetricsFactory) - + telset := telemetery.Setting{ + Logger: svc.Logger, + TracerProvider: tracer.OTEL, + Metrics: queryMetricsFactory, + ReportStatus: telemetery.HCAdapter(svc.HC()), + } // query querySrv := startQuery( svc, qOpts, qOpts.BuildQueryServiceOptions(storageFactory, logger), spanReader, dependencyReader, metricsQueryService, - queryMetricsFactory, tm, tracer, + tm, telset, ) svc.RunAndThen(func() { @@ -273,13 +279,13 @@ func startQuery( spanReader spanstore.Reader, depReader dependencystore.Reader, metricsQueryService querysvc.MetricsQueryService, - metricsFactory metrics.Factory, tm *tenancy.Manager, - jt *jtracer.JTracer, + telset telemetery.Setting, ) *queryApp.Server { - spanReader = storageMetrics.NewReadMetricsDecorator(spanReader, metricsFactory) + spanReader = storageMetrics.NewReadMetricsDecorator(spanReader, telset.Metrics) qs := querysvc.NewQueryService(spanReader, depReader, *queryOpts) - server, err := queryApp.NewServer(svc.Logger, svc.HC(), qs, metricsQueryService, qOpts, tm, jt) + + server, err := queryApp.NewServer(qs, metricsQueryService, qOpts, tm, telset) if err != nil { svc.Logger.Fatal("Could not create jaeger-query", zap.Error(err)) } diff --git a/cmd/collector/app/flags/flags.go b/cmd/collector/app/flags/flags.go index 8b3203b853c..c77814267d7 100644 --- a/cmd/collector/app/flags/flags.go +++ b/cmd/collector/app/flags/flags.go @@ -194,9 +194,9 @@ func AddFlags(flags *flag.FlagSet) { addGRPCFlags(flags, grpcServerFlagsCfg, ports.PortToHostPort(ports.CollectorGRPC)) flags.Bool(flagCollectorOTLPEnabled, true, "Enables OpenTelemetry OTLP receiver on dedicated HTTP and gRPC ports") - addHTTPFlags(flags, otlpServerFlagsCfg.HTTP, "") + addHTTPFlags(flags, otlpServerFlagsCfg.HTTP, ":4318") corsOTLPFlags.AddFlags(flags) - addGRPCFlags(flags, otlpServerFlagsCfg.GRPC, "") + addGRPCFlags(flags, otlpServerFlagsCfg.GRPC, ":4317") flags.String(flagZipkinHTTPHostPort, "", "The host:port (e.g. 127.0.0.1:9411 or :9411) of the collector's Zipkin server (disabled by default)") flags.Bool(flagZipkinKeepAliveEnabled, true, "KeepAlive configures allow Keep-Alive for Zipkin HTTP server (enabled by default)") diff --git a/cmd/collector/app/server/grpc_test.go b/cmd/collector/app/server/grpc_test.go index 487c6bc0216..27855c3ee3e 100644 --- a/cmd/collector/app/server/grpc_test.go +++ b/cmd/collector/app/server/grpc_test.go @@ -129,7 +129,6 @@ func TestCollectorReflection(t *testing.T) { grpctest.ReflectionServiceValidator{ HostPort: params.HostPortActual, - Server: server, ExpectedServices: []string{ "jaeger.api_v2.CollectorService", "jaeger.api_v2.SamplingManager", diff --git a/cmd/jaeger/config.yaml b/cmd/jaeger/config.yaml index 3be3a37af5d..dba80824d2d 100644 --- a/cmd/jaeger/config.yaml +++ b/cmd/jaeger/config.yaml @@ -1,9 +1,9 @@ service: - extensions: [jaeger_storage, jaeger_query] + extensions: [jaeger_storage, jaeger_query, remote_sampling] pipelines: traces: receivers: [otlp, jaeger, zipkin] - processors: [batch] + processors: [batch, adaptive_sampling] exporters: [jaeger_storage_exporter] extensions: @@ -14,16 +14,28 @@ extensions: # endpoint: 0.0.0.0:55679 jaeger_query: - trace_storage: memstore - trace_storage_archive: memstore_archive + trace_storage: some_store + trace_storage_archive: another_store ui_config: ./cmd/jaeger/config-ui.json jaeger_storage: - memory: - memstore: - max_traces: 100000 - memstore_archive: - max_traces: 100000 + backends: + some_store: + memory: + max_traces: 100000 + another_store: + memory: + max_traces: 100000 + + remote_sampling: + # You can either use file or adaptive sampling strategy in remote_sampling + # file: + # path: ./cmd/jaeger/sampling-strategies.json + adaptive: + sampling_store: some_store + initial_sampling_probability: 0.1 + http: + grpc: receivers: otlp: @@ -42,7 +54,10 @@ receivers: processors: batch: + # Adaptive Sampling Processor is required to support adaptive sampling. + # It expects remote_sampling extension with `adaptive:` config to be enabled. + adaptive_sampling: exporters: jaeger_storage_exporter: - trace_storage: memstore + trace_storage: some_store diff --git a/cmd/jaeger/internal/components.go b/cmd/jaeger/internal/components.go index c7b9957dbd8..f7161ffef22 100644 --- a/cmd/jaeger/internal/components.go +++ b/cmd/jaeger/internal/components.go @@ -29,7 +29,9 @@ import ( "github.com/jaegertracing/jaeger/cmd/jaeger/internal/exporters/storageexporter" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerquery" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/remotesampling" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/integration/storagecleaner" + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/processors/adaptivesampling" ) type builders struct { @@ -62,7 +64,7 @@ func (b builders) build() (otelcol.Factories, error) { jaegerquery.NewFactory(), jaegerstorage.NewFactory(), storagecleaner.NewFactory(), - // TODO add adaptive sampling + remotesampling.NewFactory(), ) if err != nil { return otelcol.Factories{}, err @@ -99,7 +101,7 @@ func (b builders) build() (otelcol.Factories, error) { batchprocessor.NewFactory(), memorylimiterprocessor.NewFactory(), // add-ons - // TODO add adaptive sampling + adaptivesampling.NewFactory(), ) if err != nil { return otelcol.Factories{}, err diff --git a/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go b/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go index 3693b8ef1fe..cfe7a2b1e50 100644 --- a/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go +++ b/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go @@ -124,8 +124,7 @@ func TestExporter(t *testing.T) { host := makeStorageExtension(t, memstoreName) - err = tracesExporter.Start(ctx, host) - require.NoError(t, err) + require.NoError(t, tracesExporter.Start(ctx, host)) defer func() { require.NoError(t, tracesExporter.Shutdown(ctx)) }() diff --git a/cmd/jaeger/internal/extension/jaegerquery/config.go b/cmd/jaeger/internal/extension/jaegerquery/config.go index e5640c2710b..ec5de1bb69c 100644 --- a/cmd/jaeger/internal/extension/jaegerquery/config.go +++ b/cmd/jaeger/internal/extension/jaegerquery/config.go @@ -6,6 +6,7 @@ package jaegerquery import ( "github.com/asaskevich/govalidator" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/config/confighttp" queryApp "github.com/jaegertracing/jaeger/cmd/query/app" @@ -18,10 +19,13 @@ var _ component.ConfigValidator = (*Config)(nil) type Config struct { queryApp.QueryOptionsBase `mapstructure:",squash"` - TraceStoragePrimary string `valid:"required" mapstructure:"trace_storage"` - TraceStorageArchive string `valid:"optional" mapstructure:"trace_storage_archive"` - confighttp.ServerConfig `mapstructure:",squash"` - Tenancy tenancy.Options `mapstructure:"multi_tenancy"` + TraceStoragePrimary string `valid:"required" mapstructure:"trace_storage"` + TraceStorageArchive string `valid:"optional" mapstructure:"trace_storage_archive"` + + HTTP confighttp.ServerConfig `mapstructure:",squash"` + GRPC configgrpc.ServerConfig `mapstructure:",squash"` + + Tenancy tenancy.Options `mapstructure:"multi_tenancy"` } func (cfg *Config) Validate() error { diff --git a/cmd/jaeger/internal/extension/jaegerquery/factory.go b/cmd/jaeger/internal/extension/jaegerquery/factory.go index 93d35781e10..9d50bc55ea5 100644 --- a/cmd/jaeger/internal/extension/jaegerquery/factory.go +++ b/cmd/jaeger/internal/extension/jaegerquery/factory.go @@ -7,7 +7,9 @@ import ( "context" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/extension" "github.com/jaegertracing/jaeger/ports" @@ -25,9 +27,15 @@ func NewFactory() extension.Factory { func createDefaultConfig() component.Config { return &Config{ - ServerConfig: confighttp.ServerConfig{ + HTTP: confighttp.ServerConfig{ Endpoint: ports.PortToHostPort(ports.QueryHTTP), }, + GRPC: configgrpc.ServerConfig{ + NetAddr: confignet.AddrConfig{ + Endpoint: ports.PortToHostPort(ports.QueryGRPC), + Transport: confignet.TransportTypeTCP, + }, + }, } } diff --git a/cmd/jaeger/internal/extension/jaegerquery/server.go b/cmd/jaeger/internal/extension/jaegerquery/server.go index dc9fc29323d..d4dfa53715d 100644 --- a/cmd/jaeger/internal/extension/jaegerquery/server.go +++ b/cmd/jaeger/internal/extension/jaegerquery/server.go @@ -10,16 +10,14 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/extension" - "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" queryApp "github.com/jaegertracing/jaeger/cmd/query/app" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" - "github.com/jaegertracing/jaeger/pkg/healthcheck" "github.com/jaegertracing/jaeger/pkg/jtracer" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/plugin/metrics/disabled" - "github.com/jaegertracing/jaeger/ports" ) var ( @@ -28,16 +26,16 @@ var ( ) type server struct { - config *Config - logger *zap.Logger - server *queryApp.Server - jtracer *jtracer.JTracer + config *Config + server *queryApp.Server + telset component.TelemetrySettings + closeTracer func(ctx context.Context) error } func newServer(config *Config, otel component.TelemetrySettings) *server { return &server{ config: config, - logger: otel.Logger, + telset: otel, } } @@ -75,22 +73,26 @@ func (s *server) Start(_ context.Context, host component.Host) error { // TODO OTel-collector does not initialize the tracer currently // https://github.com/open-telemetry/opentelemetry-collector/issues/7532 //nolint - s.jtracer, err = jtracer.New("jaeger") + tracerProvider, err := jtracer.New("jaeger") if err != nil { return fmt.Errorf("could not initialize a tracer: %w", err) } + s.closeTracer = tracerProvider.Close + telset := telemetery.Setting{ + Logger: s.telset.Logger, + TracerProvider: tracerProvider.OTEL, + ReportStatus: s.telset.ReportStatus, + } // TODO contextcheck linter complains about next line that context is not passed. It is not wrong. //nolint s.server, err = queryApp.NewServer( - s.logger, // TODO propagate healthcheck updates up to the collector's runtime - healthcheck.New(), qs, metricsQueryService, s.makeQueryOptions(), tm, - s.jtracer, + telset, ) if err != nil { return fmt.Errorf("could not create jaeger-query: %w", err) @@ -105,7 +107,7 @@ func (s *server) Start(_ context.Context, host component.Host) error { func (s *server) addArchiveStorage(opts *querysvc.QueryServiceOptions, host component.Host) error { if s.config.TraceStorageArchive == "" { - s.logger.Info("Archive storage not configured") + s.telset.Logger.Info("Archive storage not configured") return nil } @@ -114,8 +116,8 @@ func (s *server) addArchiveStorage(opts *querysvc.QueryServiceOptions, host comp return fmt.Errorf("cannot find archive storage factory: %w", err) } - if !opts.InitArchiveStorage(f, s.logger) { - s.logger.Info("Archive storage not initialized") + if !opts.InitArchiveStorage(f, s.telset.Logger) { + s.telset.Logger.Info("Archive storage not initialized") } return nil } @@ -124,9 +126,10 @@ func (s *server) makeQueryOptions() *queryApp.QueryOptions { return &queryApp.QueryOptions{ QueryOptionsBase: s.config.QueryOptionsBase, - // TODO expose via config - HTTPHostPort: ports.PortToHostPort(ports.QueryHTTP), - GRPCHostPort: ports.PortToHostPort(ports.QueryGRPC), + // TODO utilize OTEL helpers for creating HTTP/GRPC servers + HTTPHostPort: s.config.HTTP.Endpoint, + GRPCHostPort: s.config.GRPC.NetAddr.Endpoint, + // TODO handle TLS } } @@ -135,8 +138,8 @@ func (s *server) Shutdown(ctx context.Context) error { if s.server != nil { errs = append(errs, s.server.Close()) } - if s.jtracer != nil { - errs = append(errs, s.jtracer.Close(ctx)) + if s.closeTracer != nil { + errs = append(errs, s.closeTracer(ctx)) } return errors.Join(errs...) } diff --git a/cmd/jaeger/internal/extension/jaegerquery/server_test.go b/cmd/jaeger/internal/extension/jaegerquery/server_test.go index 36cc4d82c4c..6ba3e963c5c 100644 --- a/cmd/jaeger/internal/extension/jaegerquery/server_test.go +++ b/cmd/jaeger/internal/extension/jaegerquery/server_test.go @@ -6,8 +6,11 @@ package jaegerquery import ( "context" "fmt" + "net/http" "testing" + "time" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/storagetest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" @@ -17,6 +20,7 @@ import ( "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" + "github.com/jaegertracing/jaeger/internal/grpctest" "github.com/jaegertracing/jaeger/pkg/metrics" "github.com/jaegertracing/jaeger/pkg/testutils" "github.com/jaegertracing/jaeger/storage" @@ -77,27 +81,6 @@ func (fakeStorageExt) Shutdown(context.Context) error { return nil } -type storageHost struct { - extension component.Component -} - -func (storageHost) ReportFatalError(error) { -} - -func (host storageHost) GetExtensions() map[component.ID]component.Component { - return map[component.ID]component.Component{ - jaegerstorage.ID: host.extension, - } -} - -func (storageHost) GetFactory(_ component.Kind, _ component.Type) component.Factory { - return nil -} - -func (storageHost) GetExporters() map[component.DataType]map[component.ID]component.Component { - return nil -} - func TestServerDependencies(t *testing.T) { expectedDependencies := []component.ID{jaegerstorage.ID} telemetrySettings := component.TelemetrySettings{ @@ -111,9 +94,7 @@ func TestServerDependencies(t *testing.T) { } func TestServerStart(t *testing.T) { - host := storageHost{ - extension: fakeStorageExt{}, - } + host := storagetest.NewStorageHost().WithExtension(jaegerstorage.ID, fakeStorageExt{}) tests := []struct { name string config *Config @@ -160,14 +141,40 @@ func TestServerStart(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { telemetrySettings := component.TelemetrySettings{ - Logger: zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller())), + Logger: zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller())), + ReportStatus: func(*component.StatusEvent) {}, } + tt.config.HTTP.Endpoint = ":0" + tt.config.GRPC.NetAddr.Endpoint = ":0" server := newServer(tt.config, telemetrySettings) err := server.Start(context.Background(), host) - if tt.expectedErr == "" { require.NoError(t, err) defer server.Shutdown(context.Background()) + // We need to wait for servers to become available. + // Otherwise, we could call shutdown before the servers are even started, + // which could cause flaky code coverage by going through error cases. + require.Eventually(t, + func() bool { + resp, err := http.Get(fmt.Sprintf("http://%s/", server.server.HTTPAddr())) + if err != nil { + return false + } + defer resp.Body.Close() + return resp.StatusCode == http.StatusOK + }, + 10*time.Second, + 100*time.Millisecond, + "server not started") + grpctest.ReflectionServiceValidator{ + HostPort: server.server.GRPCAddr(), + ExpectedServices: []string{ + "jaeger.api_v2.QueryService", + "jaeger.api_v3.QueryService", + "jaeger.api_v2.metrics.MetricsQueryService", + "grpc.health.v1.Health", + }, + }.Execute(t) } else { require.ErrorContains(t, err, tt.expectedErr) } @@ -222,7 +229,7 @@ func TestServerAddArchiveStorage(t *testing.T) { } server := newServer(tt.config, telemetrySettings) if tt.extension != nil { - host = storageHost{extension: tt.extension} + host = storagetest.NewStorageHost().WithExtension(jaegerstorage.ID, tt.extension) } err := server.addArchiveStorage(tt.qSvcOpts, host) if tt.expectedErr == "" { diff --git a/cmd/jaeger/internal/extension/jaegerstorage/extension.go b/cmd/jaeger/internal/extension/jaegerstorage/extension.go index 9ca2dba178d..4f2e618d327 100644 --- a/cmd/jaeger/internal/extension/jaegerstorage/extension.go +++ b/cmd/jaeger/internal/extension/jaegerstorage/extension.go @@ -54,7 +54,7 @@ func GetStorageFactory(name string, host component.Host) (storage.Factory, error f, ok := comp.(Extension).Factory(name) if !ok { return nil, fmt.Errorf( - "cannot find storage '%s' declared by '%s' extension", + "cannot find definition of storage '%s' in the configuration for extension '%s'", name, componentType, ) } diff --git a/cmd/jaeger/internal/extension/jaegerstorage/extension_test.go b/cmd/jaeger/internal/extension/jaegerstorage/extension_test.go index 4d42ff4289f..b05ef36474a 100644 --- a/cmd/jaeger/internal/extension/jaegerstorage/extension_test.go +++ b/cmd/jaeger/internal/extension/jaegerstorage/extension_test.go @@ -11,6 +11,7 @@ import ( "net/http/httptest" "testing" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/storagetest" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" @@ -31,26 +32,6 @@ import ( "github.com/jaegertracing/jaeger/storage/spanstore" ) -type storageHost struct { - t *testing.T - ext component.Component -} - -func (host storageHost) GetExtensions() map[component.ID]component.Component { - return map[component.ID]component.Component{ - ID: host.ext, - } -} - -func (host storageHost) ReportFatalError(err error) { - host.t.Fatal(err) -} - -func (storageHost) GetFactory(_ component.Kind, _ component.Type) component.Factory { return nil } -func (storageHost) GetExporters() map[component.DataType]map[component.ID]component.Component { - return nil -} - type errorFactory struct { closeErr error } @@ -81,9 +62,9 @@ func TestStorageFactoryBadHostError(t *testing.T) { } func TestStorageFactoryBadNameError(t *testing.T) { - host := storageHost{t: t, ext: startStorageExtension(t, "foo")} + host := storagetest.NewStorageHost().WithExtension(ID, startStorageExtension(t, "foo")) _, err := GetStorageFactory("bar", host) - require.ErrorContains(t, err, "cannot find storage 'bar'") + require.ErrorContains(t, err, "cannot find definition of storage 'bar'") } func TestStorageFactoryBadShutdownError(t *testing.T) { @@ -105,7 +86,7 @@ func TestGetFactoryV2Error(t *testing.T) { func TestGetFactory(t *testing.T) { const name = "foo" - host := storageHost{t: t, ext: startStorageExtension(t, name)} + host := storagetest.NewStorageHost().WithExtension(ID, startStorageExtension(t, name)) f, err := GetStorageFactory(name, host) require.NoError(t, err) require.NotNil(t, f) diff --git a/cmd/jaeger/internal/extension/remotesampling/config.go b/cmd/jaeger/internal/extension/remotesampling/config.go new file mode 100644 index 00000000000..edff4707e26 --- /dev/null +++ b/cmd/jaeger/internal/extension/remotesampling/config.go @@ -0,0 +1,91 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesampling + +import ( + "errors" + + "github.com/asaskevich/govalidator" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configgrpc" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/confmap" + + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/adaptive" +) + +var ( + errNoProvider = errors.New("no sampling strategy provider specified, expecting 'adaptive' or 'file'") + errMultipleProviders = errors.New("only one sampling strategy provider can be specified, 'adaptive' or 'file'") +) + +var ( + _ component.Config = (*Config)(nil) + _ component.ConfigValidator = (*Config)(nil) + _ confmap.Unmarshaler = (*Config)(nil) +) + +type Config struct { + File *FileConfig `mapstructure:"file"` + Adaptive *AdaptiveConfig `mapstructure:"adaptive"` + HTTP *confighttp.ServerConfig `mapstructure:"http"` + GRPC *configgrpc.ServerConfig `mapstructure:"grpc"` +} + +type FileConfig struct { + // File specifies a local file as the source of sampling strategies. + Path string `valid:"required" mapstructure:"path"` +} + +type AdaptiveConfig struct { + // SamplingStore is the name of the storage defined in the jaegerstorage extension. + SamplingStore string `valid:"required" mapstructure:"sampling_store"` + + adaptive.Options `mapstructure:",squash"` +} + +// Unmarshal is a custom unmarshaler that allows the factory to provide default values +// for nested configs (like GRPC endpoint) yes still reset the pointers to nil if the +// config did not contain the corresponding sections. +// This is a workaround for the lack of opional fields support in OTEL confmap. +// Issue: https://github.com/open-telemetry/opentelemetry-collector/issues/10266 +func (cfg *Config) Unmarshal(conf *confmap.Conf) error { + // first load the config normally + err := conf.Unmarshal(cfg) + if err != nil { + return err + } + + // use string names of fields to see if they are set in the confmap + if !conf.IsSet("file") { + cfg.File = nil + } + + if !conf.IsSet("adaptive") { + cfg.Adaptive = nil + } + + if !conf.IsSet("grpc") { + cfg.GRPC = nil + } + + if !conf.IsSet("http") { + cfg.HTTP = nil + } + + return nil +} + +func (cfg *Config) Validate() error { + if cfg.File == nil && cfg.Adaptive == nil { + return errNoProvider + } + + if cfg.File != nil && cfg.Adaptive != nil { + return errMultipleProviders + } + + _, err := govalidator.ValidateStruct(cfg) + return err +} diff --git a/cmd/jaeger/internal/extension/remotesampling/config_test.go b/cmd/jaeger/internal/extension/remotesampling/config_test.go new file mode 100644 index 00000000000..c5071423c19 --- /dev/null +++ b/cmd/jaeger/internal/extension/remotesampling/config_test.go @@ -0,0 +1,134 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesampling + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/confmap" +) + +func Test_Validate(t *testing.T) { + tests := []struct { + name string + config *Config + expectedErr string + }{ + { + name: "No provider specified", + config: &Config{}, + expectedErr: "no sampling strategy provider specified, expecting 'adaptive' or 'file'", + }, + { + name: "Both providers specified", + config: &Config{ + File: &FileConfig{Path: "test-path"}, + Adaptive: &AdaptiveConfig{SamplingStore: "test-store"}, + }, + expectedErr: "only one sampling strategy provider can be specified, 'adaptive' or 'file'", + }, + { + name: "Only File provider specified", + config: &Config{ + File: &FileConfig{Path: "test-path"}, + }, + expectedErr: "", + }, + { + name: "Only Adaptive provider specified", + config: &Config{ + Adaptive: &AdaptiveConfig{SamplingStore: "test-store"}, + }, + expectedErr: "", + }, + { + name: "Invalid File provider", + config: &Config{ + File: &FileConfig{Path: ""}, + }, + expectedErr: "File.Path: non zero value required", + }, + { + name: "Invalid Adaptive provider", + config: &Config{ + Adaptive: &AdaptiveConfig{SamplingStore: ""}, + }, + expectedErr: "Adaptive.SamplingStore: non zero value required", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.config.Validate() + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + assert.Equal(t, tt.expectedErr, err.Error()) + } + }) + } +} + +func Test_Unmarshal(t *testing.T) { + tests := []struct { + name string + input map[string]any + expectedCfg *Config + expectedErr string + }{ + { + name: "Valid config with File", + input: map[string]any{ + "file": map[string]any{ + "path": "test-path", + }, + }, + expectedCfg: &Config{ + File: &FileConfig{Path: "test-path"}, + }, + expectedErr: "", + }, + { + name: "Valid config with Adaptive", + input: map[string]any{ + "adaptive": map[string]any{ + "sampling_store": "test-store", + }, + }, + expectedCfg: &Config{ + Adaptive: &AdaptiveConfig{SamplingStore: "test-store"}, + }, + expectedErr: "", + }, + { + name: "Empty config", + input: map[string]any{}, + expectedCfg: &Config{}, + expectedErr: "", + }, + { + name: "Invalid config", + input: map[string]any{ + "foo": "bar", + }, + expectedErr: "invalid keys: foo", // sensitive to lib implementation + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + conf := confmap.NewFromStringMap(tt.input) + var cfg Config + err := cfg.Unmarshal(conf) + if tt.expectedErr == "" { + require.NoError(t, err) + assert.Equal(t, tt.expectedCfg, &cfg) + } else { + assert.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} diff --git a/cmd/jaeger/internal/extension/remotesampling/extension.go b/cmd/jaeger/internal/extension/remotesampling/extension.go new file mode 100644 index 00000000000..58552d0eaab --- /dev/null +++ b/cmd/jaeger/internal/extension/remotesampling/extension.go @@ -0,0 +1,296 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesampling + +import ( + "context" + "errors" + "fmt" + "net" + "net/http" + "sync" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" + + "github.com/jaegertracing/jaeger/cmd/collector/app/sampling" + "github.com/jaegertracing/jaeger/cmd/collector/app/sampling/samplingstrategy" + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" + "github.com/jaegertracing/jaeger/pkg/clientcfg/clientcfghttp" + "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/plugin/sampling/leaderelection" + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/adaptive" + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/static" + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "github.com/jaegertracing/jaeger/storage" + "github.com/jaegertracing/jaeger/storage/samplingstore" +) + +var _ extension.Extension = (*rsExtension)(nil) + +// type Extension interface { +// extension.Extension +// // rs *rsExtension +// } + +const defaultResourceName = "sampling_store_leader" + +type rsExtension struct { + cfg *Config + telemetry component.TelemetrySettings + httpServer *http.Server + grpcServer *grpc.Server + strategyProvider samplingstrategy.Provider // TODO we should rename this to Provider, not "store" + adaptiveStore samplingstore.Store + distLock *leaderelection.DistributedElectionParticipant + shutdownWG sync.WaitGroup +} + +func newExtension(cfg *Config, telemetry component.TelemetrySettings) *rsExtension { + return &rsExtension{ + cfg: cfg, + telemetry: telemetry, + } +} + +// AdaptiveSamplingComponents is a struct that holds the components needed for adaptive sampling. +type AdaptiveSamplingComponents struct { + SamplingStore samplingstore.Store + DistLock *leaderelection.DistributedElectionParticipant + Options *adaptive.Options +} + +// GetAdaptiveSamplingComponents locates the `remotesampling` extension in Host +// and returns the sampling store and a loader/follower implementation, provided +// that the extension is configured with adaptive sampling (vs. file-based config). +func GetAdaptiveSamplingComponents(host component.Host) (*AdaptiveSamplingComponents, error) { + var comp component.Component + var compID component.ID + for id, ext := range host.GetExtensions() { + if id.Type() == ComponentType { + comp = ext + compID = id + break + } + } + if comp == nil { + return nil, fmt.Errorf( + "cannot find extension '%s' (make sure it's defined earlier in the config)", + ComponentType, + ) + } + ext, ok := comp.(*rsExtension) + if !ok { + return nil, fmt.Errorf("extension '%s' is not of type '%s'", compID, ComponentType) + } + if ext.adaptiveStore == nil || ext.distLock == nil { + return nil, fmt.Errorf("extension '%s' is not configured for adaptive sampling", compID) + } + return &AdaptiveSamplingComponents{ + SamplingStore: ext.adaptiveStore, + DistLock: ext.distLock, + Options: &ext.cfg.Adaptive.Options, + }, nil +} + +func (ext *rsExtension) Start(ctx context.Context, host component.Host) error { + if ext.cfg.File != nil { + ext.telemetry.Logger.Info( + "Starting file-based sampling strategy provider", + zap.String("path", ext.cfg.File.Path), + ) + if err := ext.startFileBasedStrategyProvider(ctx); err != nil { + return err + } + } + + if ext.cfg.Adaptive != nil { + ext.telemetry.Logger.Info( + "Starting adaptive sampling strategy provider", + zap.String("sampling_store", ext.cfg.Adaptive.SamplingStore), + ) + if err := ext.startAdaptiveStrategyProvider(host); err != nil { + return err + } + } + + if ext.cfg.HTTP != nil { + if err := ext.startHTTPServer(ctx, host); err != nil { + return fmt.Errorf("failed to start sampling http server: %w", err) + } + } + + if ext.cfg.GRPC != nil { + if err := ext.startGRPCServer(ctx, host); err != nil { + return fmt.Errorf("failed to start sampling gRPC server: %w", err) + } + } + + return nil +} + +func (ext *rsExtension) Shutdown(ctx context.Context) error { + var errs []error + + if ext.httpServer != nil { + if err := ext.httpServer.Shutdown(ctx); err != nil { + errs = append(errs, fmt.Errorf("failed to stop the sampling HTTP server: %w", err)) + } + } + + if ext.grpcServer != nil { + ext.grpcServer.GracefulStop() + } + + if ext.distLock != nil { + if err := ext.distLock.Close(); err != nil { + errs = append(errs, fmt.Errorf("failed to stop the distributed lock: %w", err)) + } + } + + if ext.strategyProvider != nil { + if err := ext.strategyProvider.Close(); err != nil { + errs = append(errs, fmt.Errorf("failed to stop strategy provider: %w", err)) + } + } + return errors.Join(errs...) +} + +func (ext *rsExtension) startFileBasedStrategyProvider(_ context.Context) error { + opts := static.Options{ + StrategiesFile: ext.cfg.File.Path, + } + + // contextcheck linter complains about next line that context is not passed. + //nolint + provider, err := static.NewProvider(opts, ext.telemetry.Logger) + if err != nil { + return fmt.Errorf("failed to create the local file strategy store: %w", err) + } + + ext.strategyProvider = provider + return nil +} + +func (ext *rsExtension) startAdaptiveStrategyProvider(host component.Host) error { + storageName := ext.cfg.Adaptive.SamplingStore + f, err := jaegerstorage.GetStorageFactory(storageName, host) + if err != nil { + return fmt.Errorf("cannot find storage factory: %w", err) + } + + storeFactory, ok := f.(storage.SamplingStoreFactory) + if !ok { + return fmt.Errorf("storage '%s' does not support sampling store", storageName) + } + + store, err := storeFactory.CreateSamplingStore(ext.cfg.Adaptive.AggregationBuckets) + if err != nil { + return fmt.Errorf("failed to create the sampling store: %w", err) + } + ext.adaptiveStore = store + + { + lock, err := storeFactory.CreateLock() + if err != nil { + return fmt.Errorf("failed to create the distributed lock: %w", err) + } + + ep := leaderelection.NewElectionParticipant(lock, defaultResourceName, + leaderelection.ElectionParticipantOptions{ + LeaderLeaseRefreshInterval: ext.cfg.Adaptive.LeaderLeaseRefreshInterval, + FollowerLeaseRefreshInterval: ext.cfg.Adaptive.FollowerLeaseRefreshInterval, + Logger: ext.telemetry.Logger, + }) + if err := ep.Start(); err != nil { + return fmt.Errorf("failed to start the leader election participant: %w", err) + } + ext.distLock = ep + } + + provider := adaptive.NewProvider(ext.cfg.Adaptive.Options, ext.telemetry.Logger, ext.distLock, store) + if err := provider.Start(); err != nil { + return fmt.Errorf("failed to start the adaptive strategy store: %w", err) + } + ext.strategyProvider = provider + return nil +} + +func (ext *rsExtension) startHTTPServer(ctx context.Context, host component.Host) error { + handler := clientcfghttp.NewHTTPHandler(clientcfghttp.HTTPHandlerParams{ + ConfigManager: &clientcfghttp.ConfigManager{ + SamplingProvider: ext.strategyProvider, + }, + MetricsFactory: metrics.NullFactory, + BasePath: "/api", // TODO is /api correct? + }) + httpMux := http.NewServeMux() + handler.RegisterRoutesWithHTTP(httpMux) + + var err error + if ext.httpServer, err = ext.cfg.HTTP.ToServer(ctx, host, ext.telemetry, httpMux); err != nil { + return err + } + + ext.telemetry.Logger.Info( + "Starting remote sampling HTTP server", + zap.String("endpoint", ext.cfg.HTTP.Endpoint), + ) + var hln net.Listener + if hln, err = ext.cfg.HTTP.ToListener(ctx); err != nil { + return err + } + + ext.shutdownWG.Add(1) + go func() { + defer ext.shutdownWG.Done() + + err := ext.httpServer.Serve(hln) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + ext.telemetry.ReportStatus(component.NewFatalErrorEvent(err)) + } + }() + + return nil +} + +func (ext *rsExtension) startGRPCServer(ctx context.Context, host component.Host) error { + var err error + if ext.grpcServer, err = ext.cfg.GRPC.ToServer(ctx, host, ext.telemetry); err != nil { + return err + } + + api_v2.RegisterSamplingManagerServer(ext.grpcServer, sampling.NewGRPCHandler(ext.strategyProvider)) + + healthServer := health.NewServer() // support health checks on the gRPC server + healthServer.SetServingStatus("jaeger.api_v2.SamplingManager", grpc_health_v1.HealthCheckResponse_SERVING) + grpc_health_v1.RegisterHealthServer(ext.grpcServer, healthServer) + + ext.telemetry.Logger.Info( + "Starting remote sampling GRPC server", + zap.String("endpoint", ext.cfg.GRPC.NetAddr.Endpoint), + ) + var gln net.Listener + if gln, err = ext.cfg.GRPC.NetAddr.Listen(ctx); err != nil { + return err + } + + ext.shutdownWG.Add(1) + go func() { + defer ext.shutdownWG.Done() + if err := ext.grpcServer.Serve(gln); err != nil && !errors.Is(err, grpc.ErrServerStopped) { + ext.telemetry.ReportStatus(component.NewFatalErrorEvent(err)) + } + }() + + return nil +} + +func (*rsExtension) Dependencies() []component.ID { + return []component.ID{jaegerstorage.ID} +} diff --git a/cmd/jaeger/internal/extension/remotesampling/extension_test.go b/cmd/jaeger/internal/extension/remotesampling/extension_test.go new file mode 100644 index 00000000000..de8cb300e24 --- /dev/null +++ b/cmd/jaeger/internal/extension/remotesampling/extension_test.go @@ -0,0 +1,252 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesampling + +import ( + "context" + "io" + "net/http" + "path/filepath" + "testing" + "time" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/storagetest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/configgrpc" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/confignet" + "go.opentelemetry.io/collector/extension" + noopmetric "go.opentelemetry.io/otel/metric/noop" + nooptrace "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" + "go.uber.org/zap/zaptest" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/adaptive" + "github.com/jaegertracing/jaeger/plugin/storage/memory" + "github.com/jaegertracing/jaeger/proto-gen/api_v2" +) + +func makeStorageExtension(t *testing.T, memstoreName string) component.Host { + telemetrySettings := component.TelemetrySettings{ + Logger: zaptest.NewLogger(t), + TracerProvider: nooptrace.NewTracerProvider(), + MeterProvider: noopmetric.NewMeterProvider(), + } + extensionFactory := jaegerstorage.NewFactory() + storageExtension, err := extensionFactory.CreateExtension( + context.Background(), + extension.Settings{ + TelemetrySettings: telemetrySettings, + }, + &jaegerstorage.Config{Backends: map[string]jaegerstorage.Backend{ + memstoreName: {Memory: &memory.Configuration{MaxTraces: 10000}}, + }}, + ) + require.NoError(t, err) + + host := storagetest.NewStorageHost() + host.WithExtension(jaegerstorage.ID, storageExtension) + + err = storageExtension.Start(context.Background(), host) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, storageExtension.Shutdown(context.Background())) }) + return host +} + +func makeRemoteSamplingExtension(t *testing.T, cfg component.Config) component.Host { + extensionFactory := NewFactory() + samplingExtension, err := extensionFactory.CreateExtension( + context.Background(), + extension.Settings{ + TelemetrySettings: component.TelemetrySettings{ + Logger: zap.L(), + TracerProvider: nooptrace.NewTracerProvider(), + }, + }, + cfg, + ) + require.NoError(t, err) + host := storagetest.NewStorageHost().WithExtension(ID, samplingExtension) + storageHost := makeStorageExtension(t, "foobar") + + require.NoError(t, samplingExtension.Start(context.Background(), storageHost)) + t.Cleanup(func() { require.NoError(t, samplingExtension.Shutdown(context.Background())) }) + return host +} + +func TestStartFileBasedProvider(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.File.Path = filepath.Join("..", "..", "..", "sampling-strategies.json") + cfg.Adaptive = nil + cfg.HTTP = nil + cfg.GRPC = nil + require.NoError(t, cfg.Validate()) + + ext, err := factory.CreateExtension(context.Background(), extension.Settings{ + TelemetrySettings: componenttest.NewNopTelemetrySettings(), + }, cfg) + require.NoError(t, err) + host := makeStorageExtension(t, "foobar") + require.NoError(t, ext.Start(context.Background(), host)) + require.NoError(t, ext.Shutdown(context.Background())) +} + +func TestStartHTTP(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.File.Path = filepath.Join("..", "..", "..", "sampling-strategies.json") + cfg.Adaptive = nil + cfg.HTTP = &confighttp.ServerConfig{ + Endpoint: "0.0.0.0:12345", + } + cfg.GRPC = nil + require.NoError(t, cfg.Validate()) + + ext, err := factory.CreateExtension(context.Background(), extension.Settings{ + TelemetrySettings: componenttest.NewNopTelemetrySettings(), + }, cfg) + require.NoError(t, err) + host := makeStorageExtension(t, "foobar") + require.NoError(t, ext.Start(context.Background(), host)) + + resp, err := http.Get("http://0.0.0.0:12345/api/sampling?service=foo") + require.NoError(t, err) + defer resp.Body.Close() + + require.Equal(t, http.StatusOK, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + expectedResponse := `{ + "probabilisticSampling": { + "samplingRate": 0.8 + }, + "strategyType": 0 + }` + require.JSONEq(t, expectedResponse, string(body)) + + require.NoError(t, ext.Shutdown(context.Background())) +} + +func TestStartGRPC(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.File.Path = filepath.Join("..", "..", "..", "sampling-strategies.json") + cfg.Adaptive = nil + cfg.HTTP = nil + cfg.GRPC = &configgrpc.ServerConfig{ + NetAddr: confignet.AddrConfig{ + Endpoint: "0.0.0.0:12346", + Transport: "tcp", + }, + } + require.NoError(t, cfg.Validate()) + + ext, err := factory.CreateExtension(context.Background(), extension.Settings{ + TelemetrySettings: componenttest.NewNopTelemetrySettings(), + }, cfg) + require.NoError(t, err) + host := makeStorageExtension(t, "foobar") + require.NoError(t, ext.Start(context.Background(), host)) + + conn, err := grpc.NewClient("0.0.0.0:12346", grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + defer conn.Close() + + c := api_v2.NewSamplingManagerClient(conn) + response, err := c.GetSamplingStrategy(context.Background(), &api_v2.SamplingStrategyParameters{ServiceName: "foo"}) + require.NoError(t, err) + require.Equal(t, 0.8, response.ProbabilisticSampling.SamplingRate) + + require.NoError(t, ext.Shutdown(context.Background())) +} + +func TestStartAdaptiveProvider(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.File = nil + cfg.Adaptive.SamplingStore = "foobar" + cfg.HTTP = nil + cfg.GRPC = nil + require.NoError(t, cfg.Validate()) + + ext, err := factory.CreateExtension(context.Background(), extension.Settings{ + TelemetrySettings: componenttest.NewNopTelemetrySettings(), + }, cfg) + require.NoError(t, err) + host := makeStorageExtension(t, "foobar") + require.NoError(t, ext.Start(context.Background(), host)) + require.NoError(t, ext.Shutdown(context.Background())) +} + +func TestStartAdaptiveStrategyProviderErrors(t *testing.T) { + host := storagetest.NewStorageHost() + ext := &rsExtension{ + cfg: &Config{ + Adaptive: &AdaptiveConfig{ + SamplingStore: "foobar", + }, + }, + } + err := ext.startAdaptiveStrategyProvider(host) + require.ErrorContains(t, err, "cannot find storage factory") +} + +func TestGetAdaptiveSamplingComponents(t *testing.T) { + // Success case + host := makeRemoteSamplingExtension(t, &Config{ + Adaptive: &AdaptiveConfig{ + SamplingStore: "foobar", + Options: adaptive.Options{ + FollowerLeaseRefreshInterval: 1, + LeaderLeaseRefreshInterval: 1, + AggregationBuckets: 1, + }, + }, + }) + + comps, err := GetAdaptiveSamplingComponents(host) + require.NoError(t, err) + assert.NotNil(t, comps.DistLock) + assert.NotNil(t, comps.SamplingStore) + assert.Equal(t, time.Duration(1), comps.Options.FollowerLeaseRefreshInterval) + assert.Equal(t, time.Duration(1), comps.Options.LeaderLeaseRefreshInterval) + assert.Equal(t, 1, comps.Options.AggregationBuckets) +} + +type wrongExtension struct{} + +func (*wrongExtension) Start(context.Context, component.Host) error { return nil } +func (*wrongExtension) Shutdown(context.Context) error { return nil } + +func TestGetAdaptiveSamplingComponentsErrors(t *testing.T) { + host := makeRemoteSamplingExtension(t, &Config{}) + _, err := GetAdaptiveSamplingComponents(host) + require.ErrorContains(t, err, "extension 'remote_sampling' is not configured for adaptive sampling") + + h1 := storagetest.NewStorageHost() + _, err = GetAdaptiveSamplingComponents(h1) + require.ErrorContains(t, err, "cannot find extension") + + h2 := h1.WithExtension(ID, &wrongExtension{}) + _, err = GetAdaptiveSamplingComponents(h2) + require.ErrorContains(t, err, "is not of type") +} + +func TestDependencies(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + ext, err := factory.CreateExtension(context.Background(), extension.Settings{ + TelemetrySettings: componenttest.NewNopTelemetrySettings(), + }, cfg) + require.NoError(t, err) + assert.Equal(t, []component.ID{jaegerstorage.ID}, ext.(*rsExtension).Dependencies()) +} diff --git a/cmd/jaeger/internal/extension/remotesampling/factory.go b/cmd/jaeger/internal/extension/remotesampling/factory.go new file mode 100644 index 00000000000..e3a1c9a5756 --- /dev/null +++ b/cmd/jaeger/internal/extension/remotesampling/factory.go @@ -0,0 +1,57 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesampling + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configgrpc" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/confignet" + "go.opentelemetry.io/collector/extension" + + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/adaptive" + "github.com/jaegertracing/jaeger/ports" +) + +// ComponentType is the name of this extension in configuration. +var ComponentType = component.MustNewType("remote_sampling") + +var ID = component.NewID(ComponentType) + +// NewFactory creates a factory for the jaeger remote sampling extension. +func NewFactory() extension.Factory { + return extension.NewFactory( + ComponentType, + createDefaultConfig, + createExtension, + component.StabilityLevelBeta, + ) +} + +func createDefaultConfig() component.Config { + return &Config{ + HTTP: &confighttp.ServerConfig{ + Endpoint: ports.PortToHostPort(ports.CollectorHTTP + 100), + }, + GRPC: &configgrpc.ServerConfig{ + NetAddr: confignet.AddrConfig{ + Endpoint: ports.PortToHostPort(ports.CollectorGRPC + 100), + Transport: confignet.TransportTypeTCP, + }, + }, + File: &FileConfig{ + Path: "", // path needs to be specified + }, + Adaptive: &AdaptiveConfig{ + SamplingStore: "", // storage name needs to be specified + Options: adaptive.DefaultOptions(), + }, + } +} + +func createExtension(_ context.Context, set extension.Settings, cfg component.Config) (extension.Extension, error) { + return newExtension(cfg.(*Config), set.TelemetrySettings), nil +} diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/factory_test.go b/cmd/jaeger/internal/extension/remotesampling/factory_test.go similarity index 71% rename from cmd/jaeger/internal/integration/receivers/storagereceiver/factory_test.go rename to cmd/jaeger/internal/extension/remotesampling/factory_test.go index 8b94fa2304b..a6de996e02e 100644 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/factory_test.go +++ b/cmd/jaeger/internal/extension/remotesampling/factory_test.go @@ -1,7 +1,7 @@ // Copyright (c) 2024 The Jaeger Authors. // SPDX-License-Identifier: Apache-2.0 -package storagereceiver +package remotesampling import ( "context" @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/receiver/receivertest" + "go.opentelemetry.io/collector/extension/extensiontest" ) func TestCreateDefaultConfig(t *testing.T) { @@ -19,10 +19,10 @@ func TestCreateDefaultConfig(t *testing.T) { require.NoError(t, componenttest.CheckConfigStruct(cfg)) } -func TestCreateTracesReceiver(t *testing.T) { +func TestCreateExtension(t *testing.T) { cfg := createDefaultConfig().(*Config) f := NewFactory() - r, err := f.CreateTracesReceiver(context.Background(), receivertest.NewNopSettings(), cfg, nil) + r, err := f.CreateExtension(context.Background(), extensiontest.NewNopSettings(), cfg) require.NoError(t, err) assert.NotNil(t, r) } diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/package_test.go b/cmd/jaeger/internal/extension/remotesampling/package_test.go similarity index 90% rename from cmd/jaeger/internal/integration/receivers/storagereceiver/package_test.go rename to cmd/jaeger/internal/extension/remotesampling/package_test.go index 4dbecd011d3..5bd9ea71735 100644 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/package_test.go +++ b/cmd/jaeger/internal/extension/remotesampling/package_test.go @@ -1,7 +1,7 @@ // Copyright (c) 2024 The Jaeger Authors. // SPDX-License-Identifier: Apache-2.0 -package storagereceiver +package remotesampling import ( "testing" diff --git a/cmd/jaeger/internal/integration/e2e_integration.go b/cmd/jaeger/internal/integration/e2e_integration.go index 0832e6d1fe3..cf590fbab3f 100644 --- a/cmd/jaeger/internal/integration/e2e_integration.go +++ b/cmd/jaeger/internal/integration/e2e_integration.go @@ -131,45 +131,34 @@ func createStorageCleanerConfig(t *testing.T, configFile string, storage string, err = yaml.Unmarshal(data, &config) require.NoError(t, err) - if injectStorageCleaner{ - // Add storage_cleaner to the service extensions - service, ok := config["service"].(map[string]any) - require.True(t, ok) - serviceExtensions, ok := service["extensions"].([]any) - require.True(t, ok) - serviceExtensions = append(serviceExtensions, "storage_cleaner") - service["extensions"] = serviceExtensions - - // Configure the storage_cleaner extension - extensions, ok := config["extensions"].(map[string]any) - require.True(t, ok) - query, ok := extensions["jaeger_query"].(map[string]any) - require.True(t, ok) - trace_storage := query["trace_storage"].(string) - extensions["storage_cleaner"] = map[string]string{"trace_storage": trace_storage} - } + serviceAny, ok := config["service"] + require.True(t, ok) + service := serviceAny.(map[string]any) + service["extensions"] = append(service["extensions"].([]any), "storage_cleaner") - // Modify the jaeger_storage settings based on the storage type - extensions, ok := config["extensions"].(map[string]any) - require.True(t, ok) - jaegerStorage, ok := extensions["jaeger_storage"].(map[string]any) + extensions, ok := config["extensions"].(map[string]any) + require.True(t, ok) + query, ok := extensions["jaeger_query"].(map[string]any) require.True(t, ok) + trace_storage := query["trace_storage"].(string) + extensions["storage_cleaner"] = map[string]string{"trace_storage": trace_storage} + + jaegerStorageAny, ok := extensions["jaeger_storage"] + require.True(t, ok) + jaegerStorage := jaegerStorageAny.(map[string]any) + backendsAny, ok := jaegerStorage["backends"] + require.True(t, ok) + backends := backendsAny.(map[string]any) switch storage { - case "elasticsearch": - elasticsearch, ok := jaegerStorage["elasticsearch"].(map[string]any) - require.True(t, ok) - esMain, ok := elasticsearch["es_main"].(map[string]any) - require.True(t, ok) + case "elasticsearch", "opensearch": + someStoreAny, ok := backends["some_storage"] + require.True(t, ok, "expecting 'some_storage' entry, found: %v", jaegerStorage) + someStore := someStoreAny.(map[string]any) + esMainAny, ok := someStore[storage] + require.True(t, ok, "expecting '%s' entry, found %v", storage, someStore) + esMain := esMainAny.(map[string]any) esMain["service_cache_ttl"] = "1ms" - - case "opensearch": - opensearch, ok := jaegerStorage["opensearch"].(map[string]any) - require.True(t, ok) - osMain, ok := opensearch["os_main"].(map[string]any) - require.True(t, ok) - osMain["service_cache_ttl"] = "1ms" - default: // Do Nothing } @@ -185,6 +174,7 @@ func createStorageCleanerConfig(t *testing.T, configFile string, storage string, func purge(t *testing.T) { addr := fmt.Sprintf("http://0.0.0.0:%s%s", storagecleaner.Port, storagecleaner.URL) + t.Logf("Purging storage via %s", addr) r, err := http.NewRequestWithContext(context.Background(), http.MethodPost, addr, nil) require.NoError(t, err) @@ -193,6 +183,8 @@ func purge(t *testing.T) { resp, err := client.Do(r) require.NoError(t, err) defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", string(body)) } diff --git a/cmd/jaeger/internal/integration/e2e_integration_test.go b/cmd/jaeger/internal/integration/e2e_integration_test.go new file mode 100644 index 00000000000..a6aaca13623 --- /dev/null +++ b/cmd/jaeger/internal/integration/e2e_integration_test.go @@ -0,0 +1,13 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import "testing" + +func TestCreateStorageCleanerConfig(t *testing.T) { + // Ensure that we can parse the existing configs correctly. + // This is faster to run than the full integration test. + createStorageCleanerConfig(t, "../../config-elasticsearch.yaml", "elasticsearch") + createStorageCleanerConfig(t, "../../config-opensearch.yaml", "opensearch") +} diff --git a/cmd/jaeger/internal/integration/es_test.go b/cmd/jaeger/internal/integration/elasticsearch_test.go similarity index 93% rename from cmd/jaeger/internal/integration/es_test.go rename to cmd/jaeger/internal/integration/elasticsearch_test.go index 64738e1d44b..421139967b7 100644 --- a/cmd/jaeger/internal/integration/es_test.go +++ b/cmd/jaeger/internal/integration/elasticsearch_test.go @@ -9,7 +9,7 @@ import ( "github.com/jaegertracing/jaeger/plugin/storage/integration" ) -func TestESStorage(t *testing.T) { +func TestElasticsearchStorage(t *testing.T) { integration.SkipUnlessEnv(t, "elasticsearch") s := &E2EStorageIntegration{ diff --git a/cmd/jaeger/internal/integration/memory_test.go b/cmd/jaeger/internal/integration/memory_test.go new file mode 100644 index 00000000000..a2592b04a38 --- /dev/null +++ b/cmd/jaeger/internal/integration/memory_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "testing" + + "github.com/jaegertracing/jaeger/plugin/storage/integration" +) + +func TestMemoryStorage(t *testing.T) { + integration.SkipUnlessEnv(t, "memory_v2") + + s := &E2EStorageIntegration{ + ConfigFile: "../../config.yaml", + StorageIntegration: integration.StorageIntegration{ + SkipArchiveTest: true, + CleanUp: purge, + }, + } + s.e2eInitialize(t, "memory") + t.Cleanup(func() { + s.e2eCleanUp(t) + }) + s.RunAll(t) +} diff --git a/cmd/jaeger/internal/integration/os_test.go b/cmd/jaeger/internal/integration/opensearch_test.go similarity index 93% rename from cmd/jaeger/internal/integration/os_test.go rename to cmd/jaeger/internal/integration/opensearch_test.go index f1273e1eb93..eca853fa8ff 100644 --- a/cmd/jaeger/internal/integration/os_test.go +++ b/cmd/jaeger/internal/integration/opensearch_test.go @@ -9,7 +9,7 @@ import ( "github.com/jaegertracing/jaeger/plugin/storage/integration" ) -func TestOSStorage(t *testing.T) { +func TestOpenSearchStorage(t *testing.T) { integration.SkipUnlessEnv(t, "opensearch") s := &E2EStorageIntegration{ ConfigFile: "../../config-opensearch.yaml", diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/README.md b/cmd/jaeger/internal/integration/receivers/storagereceiver/README.md deleted file mode 100644 index 30931adaf0f..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Storage Receiver - -`storagereceiver` is a fake receiver that creates an artificial stream of traces by: - -- repeatedly querying one of Jaeger storage backends for all traces (by service). -- tracking new traces / spans and passing them to the next component in the pipeline. - -# Getting Started - -The following settings are required: - -- `trace_storage` (no default): name of a storage backend defined in `jaegerstorage` extension - -The following settings can be optionally configured: - -- `pull_interval` (default = 0s): The delay between each iteration of pulling traces. - -```yaml -receivers: - jaeger_storage_receiver: - trace_storage: external-storage - pull_interval: 0s -``` diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/config.go b/cmd/jaeger/internal/integration/receivers/storagereceiver/config.go deleted file mode 100644 index e9319b8991d..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/config.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package storagereceiver - -import ( - "time" - - "github.com/asaskevich/govalidator" -) - -type Config struct { - TraceStorage string `valid:"required" mapstructure:"trace_storage"` - PullInterval time.Duration `mapstructure:"pull_interval"` -} - -func (cfg *Config) Validate() error { - _, err := govalidator.ValidateStruct(cfg) - return err -} diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/config_test.go b/cmd/jaeger/internal/integration/receivers/storagereceiver/config_test.go deleted file mode 100644 index 98435b3e2cf..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/config_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package storagereceiver - -import ( - "errors" - "path/filepath" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap/confmaptest" -) - -func TestLoadConfig(t *testing.T) { - t.Parallel() - - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) - require.NoError(t, err) - - tests := []struct { - id component.ID - expected component.Config - expectedErr error - }{ - { - id: component.NewIDWithName(componentType, ""), - expectedErr: errors.New("non zero value required"), - }, - { - id: component.NewIDWithName(componentType, "defaults"), - expected: &Config{ - TraceStorage: "storage", - PullInterval: 0, - }, - }, - { - id: component.NewIDWithName(componentType, "filled"), - expected: &Config{ - TraceStorage: "storage", - PullInterval: 2 * time.Second, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.id.String(), func(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - - sub, err := cm.Sub(tt.id.String()) - require.NoError(t, err) - require.NoError(t, sub.Unmarshal(cfg)) - - if tt.expectedErr != nil { - require.ErrorContains(t, component.ValidateConfig(cfg), tt.expectedErr.Error()) - } else { - require.NoError(t, component.ValidateConfig(cfg)) - assert.Equal(t, tt.expected, cfg) - } - }) - } -} diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/factory.go b/cmd/jaeger/internal/integration/receivers/storagereceiver/factory.go deleted file mode 100644 index 09abc498e72..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/factory.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package storagereceiver - -import ( - "context" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/receiver" -) - -// componentType is the name of this extension in configuration. -var componentType = component.MustNewType("jaeger_storage_receiver") - -// ID is the identifier of this extension. -var ID = component.NewID(componentType) - -func NewFactory() receiver.Factory { - return receiver.NewFactory( - componentType, - createDefaultConfig, - receiver.WithTraces(createTracesReceiver, component.StabilityLevelDevelopment), - ) -} - -func createDefaultConfig() component.Config { - return &Config{} -} - -func createTracesReceiver(_ context.Context, set receiver.Settings, config component.Config, nextConsumer consumer.Traces) (receiver.Traces, error) { - cfg := config.(*Config) - - return newTracesReceiver(cfg, set, nextConsumer) -} diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/receiver.go b/cmd/jaeger/internal/integration/receivers/storagereceiver/receiver.go deleted file mode 100644 index d6f93ef5ccd..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/receiver.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package storagereceiver - -import ( - "context" - "fmt" - "time" - - jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/receiver" - "go.uber.org/zap" - - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" - "github.com/jaegertracing/jaeger/model" - "github.com/jaegertracing/jaeger/storage/spanstore" -) - -type storageReceiver struct { - cancelConsumeLoop context.CancelFunc - config *Config - settings receiver.Settings - consumedTraces map[model.TraceID]*consumedTrace - nextConsumer consumer.Traces - spanReader spanstore.Reader -} - -type consumedTrace struct { - spanIDs map[model.SpanID]struct{} -} - -func newTracesReceiver(config *Config, set receiver.Settings, nextConsumer consumer.Traces) (*storageReceiver, error) { - return &storageReceiver{ - config: config, - settings: set, - consumedTraces: make(map[model.TraceID]*consumedTrace), - nextConsumer: nextConsumer, - }, nil -} - -func (r *storageReceiver) Start(ctx context.Context, host component.Host) error { - f, err := jaegerstorage.GetStorageFactory(r.config.TraceStorage, host) - if err != nil { - return fmt.Errorf("cannot find storage factory: %w", err) - } - - if r.spanReader, err = f.CreateSpanReader(); err != nil { - return fmt.Errorf("cannot create span reader: %w", err) - } - - ctx, cancel := context.WithCancel(ctx) - r.cancelConsumeLoop = cancel - - go func() { - if err := r.consumeLoop(ctx); err != nil { - r.settings.ReportStatus(component.NewFatalErrorEvent(err)) - } - }() - - return nil -} - -func (r *storageReceiver) consumeLoop(ctx context.Context) error { - for { - services, err := r.spanReader.GetServices(ctx) - if err != nil { - r.settings.Logger.Error("Failed to get services from consumer", zap.Error(err)) - return err - } - - for _, svc := range services { - if err := r.consumeTraces(ctx, svc); err != nil { - r.settings.Logger.Error("Failed to consume traces from consumer", zap.Error(err)) - } - } - - select { - case <-ctx.Done(): - r.settings.Logger.Info("Consumer stopped") - return nil - default: - time.Sleep(r.config.PullInterval) - } - } -} - -func (r *storageReceiver) consumeTraces(ctx context.Context, serviceName string) error { - endTime := time.Now() - traces, err := r.spanReader.FindTraces(ctx, &spanstore.TraceQueryParameters{ - ServiceName: serviceName, - StartTimeMin: endTime.Add(-1 * time.Hour), - StartTimeMax: endTime, - }) - if err != nil { - return err - } - - for _, trace := range traces { - traceID := trace.Spans[0].TraceID - if _, ok := r.consumedTraces[traceID]; !ok { - r.consumedTraces[traceID] = &consumedTrace{ - spanIDs: make(map[model.SpanID]struct{}), - } - } - r.consumeSpans(ctx, r.consumedTraces[traceID], trace.Spans) - } - - return nil -} - -func (r *storageReceiver) consumeSpans(ctx context.Context, tc *consumedTrace, spans []*model.Span) error { - // Spans are consumed one at a time because we don't know whether all spans - // in a trace have been completely exported - for _, span := range spans { - if _, ok := tc.spanIDs[span.SpanID]; !ok { - tc.spanIDs[span.SpanID] = struct{}{} - td, err := jaeger2otlp.ProtoToTraces([]*model.Batch{ - { - Spans: []*model.Span{span}, - Process: span.Process, - }, - }) - if err != nil { - return err - } - r.nextConsumer.ConsumeTraces(ctx, td) - } - } - - return nil -} - -func (r *storageReceiver) Shutdown(_ context.Context) error { - if r.cancelConsumeLoop != nil { - r.cancelConsumeLoop() - } - return nil -} diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/receiver_test.go b/cmd/jaeger/internal/integration/receivers/storagereceiver/receiver_test.go deleted file mode 100644 index 794cfdbbfd2..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/receiver_test.go +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package storagereceiver - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/storagetest" - jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" - "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/consumertest" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/collector/receiver/receivertest" - - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" - "github.com/jaegertracing/jaeger/model" - "github.com/jaegertracing/jaeger/storage" - factoryMocks "github.com/jaegertracing/jaeger/storage/mocks" - spanStoreMocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks" -) - -var _ jaegerstorage.Extension = (*mockStorageExt)(nil) - -type mockStorageExt struct { - name string - factory *factoryMocks.Factory -} - -func (*mockStorageExt) Start(context.Context, component.Host) error { - panic("not implemented") -} - -func (*mockStorageExt) Shutdown(context.Context) error { - panic("not implemented") -} - -func (m *mockStorageExt) Factory(name string) (storage.Factory, bool) { - if m.name == name { - return m.factory, true - } - return nil, false -} - -type receiverTest struct { - storageName string - receiveName string - receiveInterval time.Duration - reportStatus func(*component.StatusEvent) - - reader *spanStoreMocks.Reader - factory *factoryMocks.Factory - host *storagetest.StorageHost - receiver *storageReceiver -} - -func withReceiver( - r *receiverTest, - fn func(r *receiverTest), -) { - reader := new(spanStoreMocks.Reader) - factory := new(factoryMocks.Factory) - host := storagetest.NewStorageHost() - host.WithExtension(jaegerstorage.ID, &mockStorageExt{ - name: r.storageName, - factory: factory, - }) - cfg := createDefaultConfig().(*Config) - cfg.TraceStorage = r.receiveName - cfg.PullInterval = r.receiveInterval - receiver, _ := newTracesReceiver( - cfg, - receivertest.NewNopSettings(), - consumertest.NewNop(), - ) - receiver.settings.ReportStatus = func(_ *component.StatusEvent) {} - - r.reader = reader - r.factory = factory - r.host = host - r.receiver = receiver - fn(r) -} - -var ( - services = []string{"example-service-1", "example-service-2"} - spans = []*model.Span{ - { - TraceID: model.NewTraceID(0, 1), - SpanID: model.NewSpanID(1), - Process: &model.Process{ - ServiceName: services[0], - }, - }, - { - TraceID: model.NewTraceID(0, 1), - SpanID: model.NewSpanID(2), - Process: &model.Process{ - ServiceName: services[0], - }, - }, - { - TraceID: model.NewTraceID(0, 2), - SpanID: model.NewSpanID(3), - Process: &model.Process{ - ServiceName: services[1], - }, - }, - { - TraceID: model.NewTraceID(0, 2), - SpanID: model.NewSpanID(4), - Process: &model.Process{ - ServiceName: services[1], - }, - }, - } -) - -func TestReceiver_NoStorageError(t *testing.T) { - r := &receiverTest{ - storageName: "", - receiveName: "foo", - } - withReceiver(r, func(r *receiverTest) { - err := r.receiver.Start(context.Background(), r.host) - require.ErrorContains(t, err, "cannot find storage factory") - }) -} - -func TestReceiver_CreateSpanReaderError(t *testing.T) { - r := &receiverTest{ - storageName: "foo", - receiveName: "foo", - } - withReceiver(r, func(r *receiverTest) { - r.factory.On("CreateSpanReader").Return(nil, errors.New("mocked error")) - - err := r.receiver.Start(context.Background(), r.host) - require.ErrorContains(t, err, "cannot create span reader") - }) -} - -func TestReceiver_GetServiceError(t *testing.T) { - r := &receiverTest{ - storageName: "external-storage", - receiveName: "external-storage", - } - withReceiver(r, func(r *receiverTest) { - r.reader.On("GetServices", mock.AnythingOfType("*context.cancelCtx")).Return([]string{}, errors.New("mocked error")) - r.factory.On("CreateSpanReader").Return(r.reader, nil) - r.receiver.spanReader = r.reader - r.reportStatus = func(se *component.StatusEvent) { - require.ErrorContains(t, se.Err(), "mocked error") - } - - require.NoError(t, r.receiver.Start(context.Background(), r.host)) - }) -} - -func TestReceiver_Shutdown(t *testing.T) { - withReceiver(&receiverTest{}, func(r *receiverTest) { - require.NoError(t, r.receiver.Shutdown(context.Background())) - }) -} - -func TestReceiver_Start(t *testing.T) { - r := &receiverTest{ - storageName: "external-storage", - receiveName: "external-storage", - receiveInterval: 50 * time.Millisecond, - } - withReceiver(r, func(r *receiverTest) { - r.reader.On("GetServices", mock.AnythingOfType("*context.cancelCtx")).Return([]string{}, nil) - r.factory.On("CreateSpanReader").Return(r.reader, nil) - - require.NoError(t, r.receiver.Start(context.Background(), r.host)) - // let the consumeLoop to reach the end of iteration and sleep - time.Sleep(100 * time.Millisecond) - require.NoError(t, r.receiver.Shutdown(context.Background())) - }) -} - -func TestReceiver_StartConsume(t *testing.T) { - tests := []struct { - name string - services []string - traces []*model.Trace - tracesErr error - expectedTraces []*model.Trace - }{ - { - name: "empty service", - }, - { - name: "find traces error", - services: []string{"example-service"}, - tracesErr: errors.New("failed to find traces"), - }, - { - name: "consume first trace", - services: []string{services[0]}, - traces: []*model.Trace{ - {Spans: []*model.Span{spans[0]}}, - }, - expectedTraces: []*model.Trace{ - {Spans: []*model.Span{spans[0]}}, - }, - }, - { - name: "consume second trace", - services: services, - traces: []*model.Trace{ - {Spans: []*model.Span{spans[0]}}, - {Spans: []*model.Span{spans[2], spans[3]}}, - }, - expectedTraces: []*model.Trace{ - {Spans: []*model.Span{spans[0]}}, - {Spans: []*model.Span{spans[2]}}, - {Spans: []*model.Span{spans[3]}}, - }, - }, - { - name: "re-consume first trace with new spans", - services: services, - traces: []*model.Trace{ - {Spans: []*model.Span{spans[0], spans[1]}}, - {Spans: []*model.Span{spans[2], spans[3]}}, - }, - expectedTraces: []*model.Trace{ - {Spans: []*model.Span{spans[0]}}, - {Spans: []*model.Span{spans[2]}}, - {Spans: []*model.Span{spans[3]}}, - // span at index 1 is consumed last - {Spans: []*model.Span{spans[1]}}, - }, - }, - } - - withReceiver(&receiverTest{}, func(r *receiverTest) { - sink := &consumertest.TracesSink{} - r.receiver.nextConsumer = sink - - ctx, cancelFunc := context.WithCancel(context.Background()) - r.receiver.cancelConsumeLoop = cancelFunc - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - reader := new(spanStoreMocks.Reader) - reader.On("GetServices", mock.AnythingOfType("*context.cancelCtx")).Return(test.services, nil) - reader.On( - "FindTraces", - mock.AnythingOfType("*context.cancelCtx"), - mock.AnythingOfType("*spanstore.TraceQueryParameters"), - ).Return(test.traces, test.tracesErr) - r.receiver.spanReader = reader - - require.NoError(t, r.receiver.Shutdown(ctx)) - require.NoError(t, r.receiver.consumeLoop(ctx)) - - expectedTraces := make([]ptrace.Traces, 0) - for _, trace := range test.expectedTraces { - td, err := jaeger2otlp.ProtoToTraces([]*model.Batch{ - { - Spans: []*model.Span{trace.Spans[0]}, - Process: trace.Spans[0].Process, - }, - }) - require.NoError(t, err) - expectedTraces = append(expectedTraces, td) - } - actualTraces := sink.AllTraces() - assert.Equal(t, expectedTraces, actualTraces) - }) - } - }) -} diff --git a/cmd/jaeger/internal/integration/receivers/storagereceiver/testdata/config.yaml b/cmd/jaeger/internal/integration/receivers/storagereceiver/testdata/config.yaml deleted file mode 100644 index e590e8f1694..00000000000 --- a/cmd/jaeger/internal/integration/receivers/storagereceiver/testdata/config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -jaeger_storage_receiver: -jaeger_storage_receiver/defaults: - trace_storage: storage -jaeger_storage_receiver/filled: - trace_storage: storage - pull_interval: 2s diff --git a/cmd/jaeger/internal/integration/storagecleaner/README.md b/cmd/jaeger/internal/integration/storagecleaner/README.md index a2011cddb06..08860ac4b24 100644 --- a/cmd/jaeger/internal/integration/storagecleaner/README.md +++ b/cmd/jaeger/internal/integration/storagecleaner/README.md @@ -1,37 +1,10 @@ # storage_cleaner -This module implements an extension that allows purging the backend storage by making an HTTP POST request to it. +This module implements an extension that allows purging the backend storage by making an HTTP POST request to it. The storage_cleaner extension is intended to be used only in tests, providing a way to clear the storage between test runs. Making a POST request to the exposed endpoint will delete all data in storage. - -```mermaid -flowchart LR - Receiver --> Processor - Processor --> Exporter - JaegerStorageExension -->|"(1) get storage"| Exporter - Exporter -->|"(2) write trace"| Storage - - E2E_test -->|"(1) POST /purge"| HTTP_endpoint - JaegerStorageExension -->|"(2) getStorage()"| HTTP_endpoint - HTTP_endpoint -.->|"(3) storage.(*storage.Purger).Purge()"| Storage - - subgraph Jaeger Collector - Receiver - Processor - Exporter - - Storage - StorageCleanerExtension - HTTP_endpoint - subgraph JaegerStorageExension - Storage - end - subgraph StorageCleanerExtension - HTTP_endpoint - end - end -``` +See [Architecture Diagram](../README.md) in the parent directory. # Getting Started @@ -44,4 +17,3 @@ extensions: storage_cleaner: trace_storage: storage_name ``` - diff --git a/cmd/jaeger/internal/integration/storagecleaner/extension.go b/cmd/jaeger/internal/integration/storagecleaner/extension.go index 2a8b37016e6..c49d9b7bff0 100644 --- a/cmd/jaeger/internal/integration/storagecleaner/extension.go +++ b/cmd/jaeger/internal/integration/storagecleaner/extension.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/mux" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/extension" + "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" "github.com/jaegertracing/jaeger/storage" @@ -29,15 +30,15 @@ const ( ) type storageCleaner struct { - config *Config - server *http.Server - settings component.TelemetrySettings + config *Config + server *http.Server + telset component.TelemetrySettings } -func newStorageCleaner(config *Config, telemetrySettings component.TelemetrySettings) *storageCleaner { +func newStorageCleaner(config *Config, telset component.TelemetrySettings) *storageCleaner { return &storageCleaner{ - config: config, - settings: telemetrySettings, + config: config, + telset: telset, } } @@ -74,10 +75,11 @@ func (c *storageCleaner) Start(_ context.Context, host component.Host) error { Handler: r, ReadHeaderTimeout: 3 * time.Second, } + c.telset.Logger.Info("Starting storage cleaner server", zap.String("addr", c.server.Addr)) go func() { if err := c.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { err = fmt.Errorf("error starting cleaner server: %w", err) - c.settings.ReportStatus(component.NewFatalErrorEvent(err)) + c.telset.ReportStatus(component.NewFatalErrorEvent(err)) } }() diff --git a/cmd/jaeger/internal/integration/storagecleaner/extension_test.go b/cmd/jaeger/internal/integration/storagecleaner/extension_test.go index a150e0f00ac..6f1290cf0b9 100644 --- a/cmd/jaeger/internal/integration/storagecleaner/extension_test.go +++ b/cmd/jaeger/internal/integration/storagecleaner/extension_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.uber.org/zap/zaptest" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" "github.com/jaegertracing/jaeger/storage" @@ -84,7 +85,9 @@ func TestStorageCleanerExtension(t *testing.T) { TraceStorage: "storage", Port: Port, } - s := newStorageCleaner(config, component.TelemetrySettings{}) + s := newStorageCleaner(config, component.TelemetrySettings{ + Logger: zaptest.NewLogger(t), + }) require.NotEmpty(t, s.Dependencies()) host := storagetest.NewStorageHost() host.WithExtension(jaegerstorage.ID, &mockStorageExt{ @@ -110,7 +113,9 @@ func TestStorageCleanerExtension(t *testing.T) { func TestGetStorageFactoryError(t *testing.T) { config := &Config{} - s := newStorageCleaner(config, component.TelemetrySettings{}) + s := newStorageCleaner(config, component.TelemetrySettings{ + Logger: zaptest.NewLogger(t), + }) host := storagetest.NewStorageHost() host.WithExtension(jaegerstorage.ID, &mockStorageExt{ name: "storage", @@ -128,6 +133,7 @@ func TestStorageExtensionStartError(t *testing.T) { } var startStatus atomic.Pointer[component.StatusEvent] s := newStorageCleaner(config, component.TelemetrySettings{ + Logger: zaptest.NewLogger(t), ReportStatus: func(status *component.StatusEvent) { startStatus.Store(status) }, diff --git a/cmd/jaeger/internal/processors/adaptivesampling/config.go b/cmd/jaeger/internal/processors/adaptivesampling/config.go new file mode 100644 index 00000000000..86f3a30c8d9 --- /dev/null +++ b/cmd/jaeger/internal/processors/adaptivesampling/config.go @@ -0,0 +1,18 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adaptivesampling + +import ( + "go.opentelemetry.io/collector/component" +) + +var _ component.ConfigValidator = (*Config)(nil) + +type Config struct { + // all configuration for the processor is in the remotesampling extension +} + +func (*Config) Validate() error { + return nil +} diff --git a/cmd/jaeger/internal/processors/adaptivesampling/factory.go b/cmd/jaeger/internal/processors/adaptivesampling/factory.go new file mode 100644 index 00000000000..601dcd60317 --- /dev/null +++ b/cmd/jaeger/internal/processors/adaptivesampling/factory.go @@ -0,0 +1,48 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adaptivesampling + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/processorhelper" +) + +// componentType is the name of this extension in configuration. +var componentType = component.MustNewType("adaptive_sampling") + +// NewFactory creates a factory for the jaeger remote sampling extension. +func NewFactory() processor.Factory { + return processor.NewFactory( + componentType, + createDefaultConfig, + processor.WithTraces(createTracesProcessor, component.StabilityLevelBeta), + ) +} + +func createDefaultConfig() component.Config { + return &Config{} +} + +func createTracesProcessor( + ctx context.Context, + set processor.Settings, + cfg component.Config, + nextConsumer consumer.Traces, +) (processor.Traces, error) { + oCfg := cfg.(*Config) + sp := newTraceProcessor(*oCfg, set.TelemetrySettings) + return processorhelper.NewTracesProcessor( + ctx, + set, + cfg, + nextConsumer, + sp.processTraces, + processorhelper.WithStart(sp.start), + processorhelper.WithShutdown(sp.close), + ) +} diff --git a/cmd/jaeger/internal/processors/adaptivesampling/factory_test.go b/cmd/jaeger/internal/processors/adaptivesampling/factory_test.go new file mode 100644 index 00000000000..85e95b19cd0 --- /dev/null +++ b/cmd/jaeger/internal/processors/adaptivesampling/factory_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adaptivesampling + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/processor/processortest" +) + +func TestCreateDefaultConfig(t *testing.T) { + cfg := createDefaultConfig().(*Config) + require.NotNil(t, cfg, "failed to create default config") + require.NoError(t, componenttest.CheckConfigStruct(cfg)) + require.NoError(t, cfg.Validate()) +} + +func TestCreateTracesProcessor(t *testing.T) { + ctx := context.Background() + cfg := createDefaultConfig().(*Config) + + nextConsumer := consumertest.NewNop() + set := processortest.NewNopSettings() + + tracesProcessor, err := createTracesProcessor(ctx, set, cfg, nextConsumer) + require.NoError(t, err) + assert.NotNil(t, tracesProcessor) +} + +func TestFactoryType(t *testing.T) { + factory := NewFactory() + assert.Equal(t, componentType, factory.Type()) +} diff --git a/cmd/jaeger/internal/processors/adaptivesampling/package_test.go b/cmd/jaeger/internal/processors/adaptivesampling/package_test.go new file mode 100644 index 00000000000..10d464704eb --- /dev/null +++ b/cmd/jaeger/internal/processors/adaptivesampling/package_test.go @@ -0,0 +1,14 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adaptivesampling + +import ( + "testing" + + "github.com/jaegertracing/jaeger/pkg/testutils" +) + +func TestMain(m *testing.M) { + testutils.VerifyGoLeaks(m) +} diff --git a/cmd/jaeger/internal/processors/adaptivesampling/processor.go b/cmd/jaeger/internal/processors/adaptivesampling/processor.go new file mode 100644 index 00000000000..9573b2257a3 --- /dev/null +++ b/cmd/jaeger/internal/processors/adaptivesampling/processor.go @@ -0,0 +1,82 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adaptivesampling + +import ( + "context" + "fmt" + + otlp2jaeger "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/ptrace" + + "github.com/jaegertracing/jaeger/cmd/collector/app/sampling/samplingstrategy" + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/remotesampling" + "github.com/jaegertracing/jaeger/internal/metrics/otelmetrics" + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/adaptive" +) + +type traceProcessor struct { + config *Config + aggregator samplingstrategy.Aggregator + telset component.TelemetrySettings +} + +func newTraceProcessor(cfg Config, telset component.TelemetrySettings) *traceProcessor { + return &traceProcessor{ + config: &cfg, + telset: telset, + } +} + +func (tp *traceProcessor) start(_ context.Context, host component.Host) error { + parts, err := remotesampling.GetAdaptiveSamplingComponents(host) + if err != nil { + return fmt.Errorf( + "cannot load adaptive sampling components from `%s` extension: %w", + remotesampling.ComponentType, err) + } + + agg, err := adaptive.NewAggregator( + *parts.Options, + tp.telset.Logger, + otelmetrics.NewFactory(tp.telset.MeterProvider), + parts.DistLock, + parts.SamplingStore, + ) + if err != nil { + return fmt.Errorf("failed to create the adaptive sampling aggregator: %w", err) + } + + agg.Start() + tp.aggregator = agg + + return nil +} + +func (tp *traceProcessor) close(context.Context) error { + if tp.aggregator != nil { + if err := tp.aggregator.Close(); err != nil { + return fmt.Errorf("failed to stop the adaptive sampling aggregator : %w", err) + } + } + return nil +} + +func (tp *traceProcessor) processTraces(_ context.Context, td ptrace.Traces) (ptrace.Traces, error) { + batches, err := otlp2jaeger.ProtoFromTraces(td) + if err != nil { + return td, fmt.Errorf("cannot transform OTLP traces to Jaeger format: %w", err) + } + + for _, batch := range batches { + for _, span := range batch.Spans { + if span.Process == nil { + span.Process = batch.Process + } + adaptive.RecordThroughput(tp.aggregator, span, tp.telset.Logger) + } + } + return td, nil +} diff --git a/cmd/jaeger/internal/processors/adaptivesampling/processor_test.go b/cmd/jaeger/internal/processors/adaptivesampling/processor_test.go new file mode 100644 index 00000000000..69e7d83fe8f --- /dev/null +++ b/cmd/jaeger/internal/processors/adaptivesampling/processor_test.go @@ -0,0 +1,150 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adaptivesampling + +import ( + "context" + "errors" + "testing" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/storagetest" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension" + "go.opentelemetry.io/collector/pdata/ptrace" + noopmetric "go.opentelemetry.io/otel/metric/noop" + nooptrace "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" + "go.uber.org/zap/zaptest" + + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" + "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/remotesampling" + "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/adaptive" + "github.com/jaegertracing/jaeger/plugin/storage/memory" +) + +func makeStorageExtension(t *testing.T, memstoreName string) component.Host { + telemetrySettings := component.TelemetrySettings{ + Logger: zaptest.NewLogger(t), + TracerProvider: nooptrace.NewTracerProvider(), + MeterProvider: noopmetric.NewMeterProvider(), + } + extensionFactory := jaegerstorage.NewFactory() + storageExtension, err := extensionFactory.CreateExtension( + context.Background(), + extension.Settings{ + TelemetrySettings: telemetrySettings, + }, + &jaegerstorage.Config{Backends: map[string]jaegerstorage.Backend{ + memstoreName: {Memory: &memory.Configuration{MaxTraces: 10000}}, + }}, + ) + require.NoError(t, err) + + host := storagetest.NewStorageHost() + host.WithExtension(jaegerstorage.ID, storageExtension) + + err = storageExtension.Start(context.Background(), host) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, storageExtension.Shutdown(context.Background())) }) + return host +} + +var _ component.Config = (*Config)(nil) + +func makeRemoteSamplingExtension(t *testing.T, cfg component.Config) component.Host { + extensionFactory := remotesampling.NewFactory() + samplingExtension, err := extensionFactory.CreateExtension( + context.Background(), + extension.Settings{ + TelemetrySettings: component.TelemetrySettings{ + Logger: zap.L(), + TracerProvider: nooptrace.NewTracerProvider(), + }, + }, + cfg, + ) + require.NoError(t, err) + host := storagetest.NewStorageHost().WithExtension(remotesampling.ID, samplingExtension) + storageHost := makeStorageExtension(t, "foobar") + + err = samplingExtension.Start(context.Background(), storageHost) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, samplingExtension.Shutdown(context.Background())) }) + return host +} + +func TestNewTraceProcessor(t *testing.T) { + telemetrySettings := component.TelemetrySettings{ + Logger: zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller())), + } + config, ok := createDefaultConfig().(*Config) + require.True(t, ok) + newTraceProcessor := newTraceProcessor(*config, telemetrySettings) + require.NotNil(t, newTraceProcessor) +} + +func TestTraceProcessor(t *testing.T) { + telemetrySettings := component.TelemetrySettings{ + Logger: zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller())), + MeterProvider: noopmetric.NewMeterProvider(), + } + config := createDefaultConfig().(*Config) + traceProcessor := newTraceProcessor(*config, telemetrySettings) + + rsCfg := &remotesampling.Config{ + Adaptive: &remotesampling.AdaptiveConfig{ + SamplingStore: "foobar", + Options: adaptive.DefaultOptions(), + }, + } + host := makeRemoteSamplingExtension(t, rsCfg) + + rsCfg.Adaptive.Options.AggregationBuckets = 0 + err := traceProcessor.start(context.Background(), host) + require.ErrorContains(t, err, "AggregationBuckets must be greater than 0") + + rsCfg.Adaptive.Options = adaptive.DefaultOptions() + require.NoError(t, traceProcessor.start(context.Background(), host)) + + twww := makeTracesOneSpan() + trace, err := traceProcessor.processTraces(context.Background(), twww) + require.NoError(t, err) + require.NotNil(t, trace) + + err = traceProcessor.close(context.Background()) + require.NoError(t, err) +} + +func makeTracesOneSpan() ptrace.Traces { + traces := ptrace.NewTraces() + rSpans := traces.ResourceSpans().AppendEmpty() + sSpans := rSpans.ScopeSpans().AppendEmpty() + span := sSpans.Spans().AppendEmpty() + span.SetName("test") + return traces +} + +func TestGetAdaptiveSamplingComponentsError(t *testing.T) { + processor := &traceProcessor{} + err := processor.start(context.Background(), storagetest.NewStorageHost()) + require.ErrorContains(t, err, "cannot load adaptive sampling components") +} + +// aggregator that returns error from Close() +type notClosingAgg struct{} + +func (*notClosingAgg) Close() error { return errors.New("not closing") } + +func (*notClosingAgg) HandleRootSpan(*model.Span, *zap.Logger) {} +func (*notClosingAgg) RecordThroughput(string, string, model.SamplerType, float64) {} +func (*notClosingAgg) Start() {} + +func TestTraceProcessorCloseError(t *testing.T) { + processor := &traceProcessor{ + aggregator: ¬ClosingAgg{}, + } + require.ErrorContains(t, processor.close(context.Background()), "not closing") +} diff --git a/cmd/jaeger/sampling-strategies.json b/cmd/jaeger/sampling-strategies.json new file mode 100644 index 00000000000..6928e6d0436 --- /dev/null +++ b/cmd/jaeger/sampling-strategies.json @@ -0,0 +1,18 @@ +{ + "default_strategy": { + "type": "probabilistic", + "param": 0.1 + }, + "service_strategies": [ + { + "service": "foo", + "type": "probabilistic", + "param": 0.8 + }, + { + "service": "bar", + "type": "ratelimiting", + "param": 1 + } + ] +} diff --git a/cmd/query/app/apiv3/http_gateway.go b/cmd/query/app/apiv3/http_gateway.go index 8aca4a511a2..3b0884c7f12 100644 --- a/cmd/query/app/apiv3/http_gateway.go +++ b/cmd/query/app/apiv3/http_gateway.go @@ -17,12 +17,12 @@ import ( "github.com/gorilla/mux" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel/trace" "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/query/app/internal/api_v3" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" "github.com/jaegertracing/jaeger/model" - "github.com/jaegertracing/jaeger/pkg/jtracer" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/storage/spanstore" ) @@ -48,7 +48,7 @@ type HTTPGateway struct { QueryService *querysvc.QueryService TenancyMgr *tenancy.Manager Logger *zap.Logger - Tracer *jtracer.JTracer + Tracer trace.TracerProvider } // RegisterRoutes registers HTTP endpoints for APIv3 into provided mux. @@ -75,7 +75,7 @@ func (h *HTTPGateway) addRoute( traceMiddleware := otelhttp.NewHandler( otelhttp.WithRouteTag(route, handler), route, - otelhttp.WithTracerProvider(h.Tracer.OTEL)) + otelhttp.WithTracerProvider(h.Tracer)) return router.HandleFunc(route, traceMiddleware.ServeHTTP) } diff --git a/cmd/query/app/apiv3/http_gateway_test.go b/cmd/query/app/apiv3/http_gateway_test.go index 6cd62ba492b..d747bb799ff 100644 --- a/cmd/query/app/apiv3/http_gateway_test.go +++ b/cmd/query/app/apiv3/http_gateway_test.go @@ -46,7 +46,7 @@ func setupHTTPGatewayNoServer( QueryService: q, TenancyMgr: tenancy.NewManager(&tenancyOptions), Logger: zap.NewNop(), - Tracer: jtracer.NoOp(), + Tracer: jtracer.NoOp().OTEL, } gw.router = &mux.Router{} diff --git a/cmd/query/app/handler_archive_test.go b/cmd/query/app/handler_archive_test.go index 9b48941d0cb..82f0daa721c 100644 --- a/cmd/query/app/handler_archive_test.go +++ b/cmd/query/app/handler_archive_test.go @@ -48,7 +48,7 @@ func TestGetArchivedTrace_NotFound(t *testing.T) { } { tc := tc // capture loop var t.Run(tc.name, func(t *testing.T) { - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, spanstore.ErrTraceNotFound).Once() var response structuredResponse @@ -66,7 +66,7 @@ func TestGetArchivedTraceSuccess(t *testing.T) { mockReader := &spanstoremocks.Reader{} mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Once() - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { // make main reader return NotFound ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, spanstore.ErrTraceNotFound).Once() @@ -81,7 +81,7 @@ func TestGetArchivedTraceSuccess(t *testing.T) { // Test failure in parsing trace ID. func TestArchiveTrace_BadTraceID(t *testing.T) { - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { var response structuredResponse err := postJSON(ts.server.URL+"/api/archive/badtraceid", []string{}, &response) require.Error(t, err) @@ -95,7 +95,7 @@ func TestArchiveTrace_TraceNotFound(t *testing.T) { Return(nil, spanstore.ErrTraceNotFound).Once() mockWriter := &spanstoremocks.Writer{} // Not actually going to write the trace, so no need to define mockWriter action - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { // make main reader return NotFound ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, spanstore.ErrTraceNotFound).Once() @@ -106,7 +106,7 @@ func TestArchiveTrace_TraceNotFound(t *testing.T) { } func TestArchiveTrace_NoStorage(t *testing.T) { - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { var response structuredResponse err := postJSON(ts.server.URL+"/api/archive/"+mockTraceID.String(), []string{}, &response) require.EqualError(t, err, `500 error from server: {"data":null,"total":0,"limit":0,"offset":0,"errors":[{"code":500,"msg":"archive span storage was not configured"}]}`+"\n") @@ -117,7 +117,7 @@ func TestArchiveTrace_Success(t *testing.T) { mockWriter := &spanstoremocks.Writer{} mockWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(nil).Times(2) - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Once() var response structuredResponse @@ -130,7 +130,7 @@ func TestArchiveTrace_WriteErrors(t *testing.T) { mockWriter := &spanstoremocks.Writer{} mockWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(errors.New("cannot save")).Times(2) - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Once() var response structuredResponse diff --git a/cmd/query/app/handler_deps_test.go b/cmd/query/app/handler_deps_test.go index 182e149f94d..9d6e064855d 100644 --- a/cmd/query/app/handler_deps_test.go +++ b/cmd/query/app/handler_deps_test.go @@ -310,8 +310,7 @@ func TestFilterDependencies(t *testing.T) { } func TestGetDependenciesSuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) expectedDependencies := []model.DependencyLink{{Parent: "killer", Child: "queen", CallCount: 12}} endTs := time.Unix(0, 1476374248550*millisToNanosMultiplier) ts.dependencyReader.On("GetDependencies", @@ -332,10 +331,12 @@ func TestGetDependenciesSuccess(t *testing.T) { } func TestGetDependenciesCassandraFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) endTs := time.Unix(0, 1476374248550*millisToNanosMultiplier) - ts.dependencyReader.On("GetDependencies", endTs, defaultDependencyLookbackDuration).Return(nil, errStorage).Times(1) + ts.dependencyReader.On("GetDependencies", + mock.Anything, // context + endTs, + defaultDependencyLookbackDuration).Return(nil, errStorage).Times(1) var response structuredResponse err := getJSON(ts.server.URL+"/api/dependencies?endTs=1476374248550&service=testing", &response) @@ -343,18 +344,14 @@ func TestGetDependenciesCassandraFailure(t *testing.T) { } func TestGetDependenciesEndTimeParsingFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() - + ts := initializeTestServer(t) var response structuredResponse err := getJSON(ts.server.URL+"/api/dependencies?endTs=shazbot&service=testing", &response) require.Error(t, err) } func TestGetDependenciesLookbackParsingFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() - + ts := initializeTestServer(t) var response structuredResponse err := getJSON(ts.server.URL+"/api/dependencies?endTs=1476374248550&service=testing&lookback=shazbot", &response) require.Error(t, err) diff --git a/cmd/query/app/handler_options.go b/cmd/query/app/handler_options.go index 853a41a2222..354f6a5fc91 100644 --- a/cmd/query/app/handler_options.go +++ b/cmd/query/app/handler_options.go @@ -18,10 +18,10 @@ package app import ( "time" + "go.opentelemetry.io/otel/trace" "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" - "github.com/jaegertracing/jaeger/pkg/jtracer" ) // HandlerOption is a function that sets some option on the APIHandler @@ -62,7 +62,7 @@ func (handlerOptions) QueryLookbackDuration(queryLookbackDuration time.Duration) } // Tracer creates a HandlerOption that passes the tracer to the handler -func (handlerOptions) Tracer(tracer *jtracer.JTracer) HandlerOption { +func (handlerOptions) Tracer(tracer trace.TracerProvider) HandlerOption { return func(apiHandler *APIHandler) { apiHandler.tracer = tracer } diff --git a/cmd/query/app/http_handler.go b/cmd/query/app/http_handler.go index 5f217118610..58ecb875e33 100644 --- a/cmd/query/app/http_handler.go +++ b/cmd/query/app/http_handler.go @@ -30,6 +30,7 @@ import ( "github.com/gorilla/mux" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" @@ -90,7 +91,7 @@ type APIHandler struct { basePath string apiPrefix string logger *zap.Logger - tracer *jtracer.JTracer + tracer trace.TracerProvider } // NewAPIHandler returns an APIHandler @@ -114,7 +115,7 @@ func NewAPIHandler(queryService *querysvc.QueryService, tm *tenancy.Manager, opt aH.logger = zap.NewNop() } if aH.tracer == nil { - aH.tracer = jtracer.NoOp() + aH.tracer = jtracer.NoOp().OTEL } return aH } @@ -151,7 +152,7 @@ func (aH *APIHandler) handleFunc( traceMiddleware := otelhttp.NewHandler( otelhttp.WithRouteTag(route, traceResponseHandler(handler)), route, - otelhttp.WithTracerProvider(aH.tracer.OTEL)) + otelhttp.WithTracerProvider(aH.tracer)) return router.HandleFunc(route, traceMiddleware.ServeHTTP) } diff --git a/cmd/query/app/http_handler_test.go b/cmd/query/app/http_handler_test.go index ec84b37e55a..680420023eb 100644 --- a/cmd/query/app/http_handler_test.go +++ b/cmd/query/app/http_handler_test.go @@ -38,7 +38,8 @@ import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.uber.org/zap" - "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "go.uber.org/zap/zaptest/observer" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" "github.com/jaegertracing/jaeger/model" @@ -102,8 +103,9 @@ type structuredTraceResponse struct { Errors []structuredError `json:"errors"` } -func initializeTestServerWithHandler(queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) *testServer { +func initializeTestServerWithHandler(t *testing.T, queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) *testServer { return initializeTestServerWithOptions( + t, &tenancy.Manager{}, queryOptions, append( @@ -119,23 +121,33 @@ func initializeTestServerWithHandler(queryOptions querysvc.QueryServiceOptions, ) } -func initializeTestServerWithOptions(tenancyMgr *tenancy.Manager, queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) *testServer { +func initializeTestServerWithOptions( + t *testing.T, + tenancyMgr *tenancy.Manager, + queryOptions querysvc.QueryServiceOptions, + options ...HandlerOption, +) *testServer { + options = append(options, HandlerOptions.Logger(zaptest.NewLogger(t))) readStorage := &spanstoremocks.Reader{} dependencyStorage := &depsmocks.Reader{} qs := querysvc.NewQueryService(readStorage, dependencyStorage, queryOptions) r := NewRouter() handler := NewAPIHandler(qs, tenancyMgr, options...) handler.RegisterRoutes(r) - return &testServer{ + ts := &testServer{ server: httptest.NewServer(tenancy.ExtractTenantHTTPHandler(tenancyMgr, r)), spanReader: readStorage, dependencyReader: dependencyStorage, handler: handler, } + t.Cleanup(func() { + ts.server.Close() + }) + return ts } -func initializeTestServer(options ...HandlerOption) *testServer { - return initializeTestServerWithHandler(querysvc.QueryServiceOptions{}, options...) +func initializeTestServer(t *testing.T, options ...HandlerOption) *testServer { + return initializeTestServerWithHandler(t, querysvc.QueryServiceOptions{}, options...) } type testServer struct { @@ -145,15 +157,13 @@ type testServer struct { server *httptest.Server } -func withTestServer(doTest func(s *testServer), queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) { - ts := initializeTestServerWithOptions(&tenancy.Manager{}, queryOptions, options...) - defer ts.server.Close() +func withTestServer(t *testing.T, doTest func(s *testServer), queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) { + ts := initializeTestServerWithOptions(t, &tenancy.Manager{}, queryOptions, options...) doTest(ts) } func TestGetTraceSuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Once() @@ -163,44 +173,20 @@ func TestGetTraceSuccess(t *testing.T) { assert.Empty(t, response.Errors) } -type logData struct { - e zapcore.Entry - f []zapcore.Field -} - -type testLogger struct { - logs *[]logData -} - -func (testLogger) Enabled(zapcore.Level) bool { return true } -func (l testLogger) With([]zapcore.Field) zapcore.Core { return l } -func (testLogger) Sync() error { return nil } -func (l testLogger) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { - return ce.AddCore(e, l) -} - -func (l testLogger) Write(e zapcore.Entry, f []zapcore.Field) error { - *l.logs = append(*l.logs, logData{e: e, f: f}) - return nil -} - func TestLogOnServerError(t *testing.T) { - l := &testLogger{ - logs: &[]logData{}, - } + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) readStorage := &spanstoremocks.Reader{} dependencyStorage := &depsmocks.Reader{} qs := querysvc.NewQueryService(readStorage, dependencyStorage, querysvc.QueryServiceOptions{}) - apiHandlerOptions := []HandlerOption{ - HandlerOptions.Logger(zap.New(l)), - } - h := NewAPIHandler(qs, &tenancy.Manager{}, apiHandlerOptions...) + h := NewAPIHandler(qs, &tenancy.Manager{}, HandlerOptions.Logger(logger)) e := errors.New("test error") h.handleError(&httptest.ResponseRecorder{}, e, http.StatusInternalServerError) - require.Len(t, *l.logs, 1) - assert.Equal(t, "HTTP handler, Internal Server Error", (*l.logs)[0].e.Message) - assert.Len(t, (*l.logs)[0].f, 1) - assert.Equal(t, e, (*l.logs)[0].f[0].Interface) + require.Len(t, logs.All(), 1) + log := logs.All()[0] + assert.Equal(t, "HTTP handler, Internal Server Error", log.Message) + require.Len(t, log.Context, 1) + assert.Equal(t, e, log.Context[0].Interface) } // httpResponseErrWriter implements the http.ResponseWriter interface that returns an error on Write. @@ -321,8 +307,7 @@ func TestGetTrace(t *testing.T) { jTracer := jtracer.JTracer{OTEL: tracerProvider} defer tracerProvider.Shutdown(context.Background()) - ts := initializeTestServer(HandlerOptions.Tracer(&jTracer)) - defer ts.server.Close() + ts := initializeTestServer(t, HandlerOptions.Tracer(jTracer.OTEL)) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), model.NewTraceID(0, 0x123456abc)). Return(makeMockTrace(t), nil).Once() @@ -343,8 +328,7 @@ func TestGetTrace(t *testing.T) { } func TestGetTraceDBFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, errStorage).Once() @@ -354,8 +338,7 @@ func TestGetTraceDBFailure(t *testing.T) { } func TestGetTraceNotFound(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, spanstore.ErrTraceNotFound).Once() @@ -366,13 +349,13 @@ func TestGetTraceNotFound(t *testing.T) { func TestGetTraceAdjustmentFailure(t *testing.T) { ts := initializeTestServerWithHandler( + t, querysvc.QueryServiceOptions{ Adjuster: adjuster.Func(func(trace *model.Trace) (*model.Trace, error) { return trace, errAdjustment }), }, ) - defer ts.server.Close() ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Once() @@ -384,8 +367,7 @@ func TestGetTraceAdjustmentFailure(t *testing.T) { } func TestGetTraceBadTraceID(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) var response structuredResponse err := getJSON(ts.server.URL+`/api/traces/chumbawumba`, &response) @@ -393,8 +375,7 @@ func TestGetTraceBadTraceID(t *testing.T) { } func TestSearchSuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("FindTraces", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*spanstore.TraceQueryParameters")). Return([]*model.Trace{mockTrace}, nil).Once() @@ -405,8 +386,7 @@ func TestSearchSuccess(t *testing.T) { } func TestSearchByTraceIDSuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Twice() @@ -419,10 +399,9 @@ func TestSearchByTraceIDSuccess(t *testing.T) { func TestSearchByTraceIDSuccessWithArchive(t *testing.T) { archiveReadMock := &spanstoremocks.Reader{} - ts := initializeTestServerWithOptions(&tenancy.Manager{}, querysvc.QueryServiceOptions{ + ts := initializeTestServerWithOptions(t, &tenancy.Manager{}, querysvc.QueryServiceOptions{ ArchiveSpanReader: archiveReadMock, }) - defer ts.server.Close() ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, spanstore.ErrTraceNotFound).Twice() archiveReadMock.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). @@ -436,8 +415,7 @@ func TestSearchByTraceIDSuccessWithArchive(t *testing.T) { } func TestSearchByTraceIDNotFound(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, spanstore.ErrTraceNotFound).Once() @@ -449,8 +427,7 @@ func TestSearchByTraceIDNotFound(t *testing.T) { } func TestSearchByTraceIDFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) whatsamattayou := "https://youtu.be/WrKFOCg13QQ" ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(nil, fmt.Errorf(whatsamattayou)).Once() @@ -462,6 +439,7 @@ func TestSearchByTraceIDFailure(t *testing.T) { func TestSearchModelConversionFailure(t *testing.T) { ts := initializeTestServerWithOptions( + t, &tenancy.Manager{}, querysvc.QueryServiceOptions{ Adjuster: adjuster.Func(func(trace *model.Trace) (*model.Trace, error) { @@ -469,7 +447,6 @@ func TestSearchModelConversionFailure(t *testing.T) { }), }, ) - defer ts.server.Close() ts.spanReader.On("FindTraces", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*spanstore.TraceQueryParameters")). Return([]*model.Trace{mockTrace}, nil).Once() var response structuredResponse @@ -480,8 +457,7 @@ func TestSearchModelConversionFailure(t *testing.T) { } func TestSearchDBFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("FindTraces", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*spanstore.TraceQueryParameters")). Return(nil, fmt.Errorf("whatsamattayou")).Once() @@ -510,8 +486,7 @@ func TestSearchFailures(t *testing.T) { } func testIndividualSearchFailures(t *testing.T, urlStr, errMsg string) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("Query", mock.AnythingOfType("spanstore.TraceQueryParameters")). Return([]*model.Trace{}, nil).Once() @@ -521,8 +496,7 @@ func testIndividualSearchFailures(t *testing.T, urlStr, errMsg string) { } func TestGetServicesSuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) expectedServices := []string{"trifle", "bling"} ts.spanReader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(expectedServices, nil).Once() @@ -537,8 +511,7 @@ func TestGetServicesSuccess(t *testing.T) { } func TestGetServicesStorageFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(nil, errStorage).Once() var response structuredResponse @@ -547,8 +520,7 @@ func TestGetServicesStorageFailure(t *testing.T) { } func TestGetOperationsSuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) expectedOperations := []spanstore.Operation{{Name: ""}, {Name: "get", SpanKind: "server"}} ts.spanReader.On( "GetOperations", @@ -577,17 +549,14 @@ func TestGetOperationsSuccess(t *testing.T) { } func TestGetOperationsNoServiceName(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() - + ts := initializeTestServer(t) var response structuredResponse err := getJSON(ts.server.URL+"/api/operations", &response) require.Error(t, err) } func TestGetOperationsStorageFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On( "GetOperations", mock.AnythingOfType("*context.valueCtx"), @@ -599,8 +568,7 @@ func TestGetOperationsStorageFailure(t *testing.T) { } func TestGetOperationsLegacySuccess(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) expectedOperationNames := []string{"", "get"} expectedOperations := []spanstore.Operation{ {Name: ""}, @@ -621,8 +589,7 @@ func TestGetOperationsLegacySuccess(t *testing.T) { } func TestGetOperationsLegacyStorageFailure(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) ts.spanReader.On( "GetOperations", mock.AnythingOfType("*context.valueCtx"), @@ -641,7 +608,7 @@ func TestTransformOTLPSuccess(t *testing.T) { require.NoError(t, err) return out } - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { inFile, err := os.Open("./fixture/otlp2jaeger-in.json") require.NoError(t, err) @@ -661,7 +628,7 @@ func TestTransformOTLPSuccess(t *testing.T) { } func TestTransformOTLPReadError(t *testing.T) { - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { bytesReader := &IoReaderMock{} bytesReader.On("Read", mock.AnythingOfType("[]uint8")).Return(0, errors.New("Mocked error")) _, err := ts.server.Client().Post(ts.server.URL+"/api/transform", "application/json", bytesReader) @@ -670,7 +637,7 @@ func TestTransformOTLPReadError(t *testing.T) { } func TestTransformOTLPBadPayload(t *testing.T) { - withTestServer(func(ts *testServer) { + withTestServer(t, func(ts *testServer) { response := new(any) request := "Bad Payload" err := postJSON(ts.server.URL+"/api/transform", request, response) @@ -683,8 +650,7 @@ func TestGetMetricsSuccess(t *testing.T) { apiHandlerOptions := []HandlerOption{ HandlerOptions.MetricsQueryService(mr), } - ts := initializeTestServer(apiHandlerOptions...) - defer ts.server.Close() + ts := initializeTestServer(t, apiHandlerOptions...) expectedLabel := &metrics.Label{ Name: "service_name", Value: "emailservice", @@ -766,11 +732,7 @@ func TestGetMetricsSuccess(t *testing.T) { func TestMetricsReaderError(t *testing.T) { metricsReader := &metricsmocks.Reader{} - apiHandlerOptions := []HandlerOption{ - HandlerOptions.MetricsQueryService(metricsReader), - } - ts := initializeTestServer(apiHandlerOptions...) - defer ts.server.Close() + ts := initializeTestServer(t, HandlerOptions.MetricsQueryService(metricsReader)) for _, tc := range []struct { name string @@ -818,10 +780,7 @@ func TestMetricsQueryDisabled(t *testing.T) { disabledReader, err := disabled.NewMetricsReader() require.NoError(t, err) - apiHandlerOptions := []HandlerOption{ - HandlerOptions.MetricsQueryService(disabledReader), - } - ts := initializeTestServer(apiHandlerOptions...) + ts := initializeTestServer(t, HandlerOptions.MetricsQueryService(disabledReader)) defer ts.server.Close() for _, tc := range []struct { @@ -854,10 +813,7 @@ func TestMetricsQueryDisabled(t *testing.T) { func TestGetMinStep(t *testing.T) { metricsReader := &metricsmocks.Reader{} - apiHandlerOptions := []HandlerOption{ - HandlerOptions.MetricsQueryService(metricsReader), - } - ts := initializeTestServer(apiHandlerOptions...) + ts := initializeTestServer(t, HandlerOptions.MetricsQueryService(metricsReader)) defer ts.server.Close() // Prepare metricsReader.On( @@ -946,10 +902,9 @@ func TestSearchTenancyHTTP(t *testing.T) { tenancyOptions := tenancy.Options{ Enabled: true, } - ts := initializeTestServerWithOptions( + ts := initializeTestServerWithOptions(t, tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) - defer ts.server.Close() ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Twice() @@ -973,10 +928,7 @@ func TestSearchTenancyRejectionHTTP(t *testing.T) { tenancyOptions := tenancy.Options{ Enabled: true, } - ts := initializeTestServerWithOptions( - tenancy.NewManager(&tenancyOptions), - querysvc.QueryServiceOptions{}) - defer ts.server.Close() + ts := initializeTestServerWithOptions(t, tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). Return(mockTrace, nil).Twice() @@ -1002,10 +954,7 @@ func TestSearchTenancyFlowTenantHTTP(t *testing.T) { tenancyOptions := tenancy.Options{ Enabled: true, } - ts := initializeTestServerWithOptions( - tenancy.NewManager(&tenancyOptions), - querysvc.QueryServiceOptions{}) - defer ts.server.Close() + ts := initializeTestServerWithOptions(t, tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) ts.spanReader.On("GetTrace", mock.MatchedBy(func(v any) bool { ctx, ok := v.(context.Context) if !ok || tenancy.GetTenant(ctx) != "acme" { diff --git a/cmd/query/app/query_parser_test.go b/cmd/query/app/query_parser_test.go index e75d448a144..45fa6ea454c 100644 --- a/cmd/query/app/query_parser_test.go +++ b/cmd/query/app/query_parser_test.go @@ -256,8 +256,7 @@ func TestParseRepeatedSpanKinds(t *testing.T) { } func TestParameterErrors(t *testing.T) { - ts := initializeTestServer() - defer ts.server.Close() + ts := initializeTestServer(t) for _, tc := range []struct { name string diff --git a/cmd/query/app/server.go b/cmd/query/app/server.go index c2debad540a..f44d685c470 100644 --- a/cmd/query/app/server.go +++ b/cmd/query/app/server.go @@ -26,6 +26,7 @@ import ( "github.com/gorilla/handlers" "github.com/soheilhy/cmux" + "go.opentelemetry.io/collector/component" "go.uber.org/zap" "go.uber.org/zap/zapcore" "google.golang.org/grpc" @@ -38,10 +39,9 @@ import ( "github.com/jaegertracing/jaeger/cmd/query/app/internal/api_v3" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" "github.com/jaegertracing/jaeger/pkg/bearertoken" - "github.com/jaegertracing/jaeger/pkg/healthcheck" - "github.com/jaegertracing/jaeger/pkg/jtracer" "github.com/jaegertracing/jaeger/pkg/netutils" "github.com/jaegertracing/jaeger/pkg/recoveryhandler" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/proto-gen/api_v2" "github.com/jaegertracing/jaeger/proto-gen/api_v2/metrics" @@ -49,13 +49,9 @@ import ( // Server runs HTTP, Mux and a grpc server type Server struct { - logger *zap.Logger - healthCheck *healthcheck.HealthCheck querySvc *querysvc.QueryService queryOptions *QueryOptions - tracer *jtracer.JTracer // TODO make part of flags.Service - conn net.Listener grpcConn net.Listener httpConn net.Listener @@ -64,10 +60,16 @@ type Server struct { httpServer *httpServer separatePorts bool bgFinished sync.WaitGroup + telemetery.Setting } // NewServer creates and initializes Server -func NewServer(logger *zap.Logger, healthCheck *healthcheck.HealthCheck, querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.Manager, tracer *jtracer.JTracer) (*Server, error) { +func NewServer(querySvc *querysvc.QueryService, + metricsQuerySvc querysvc.MetricsQueryService, + options *QueryOptions, + tm *tenancy.Manager, + telset telemetery.Setting, +) (*Server, error) { _, httpPort, err := net.SplitHostPort(options.HTTPHostPort) if err != nil { return nil, fmt.Errorf("invalid HTTP server host:port: %w", err) @@ -76,38 +78,37 @@ func NewServer(logger *zap.Logger, healthCheck *healthcheck.HealthCheck, querySv if err != nil { return nil, fmt.Errorf("invalid gRPC server host:port: %w", err) } + separatePorts := grpcPort != httpPort || grpcPort == "0" || httpPort == "0" - if (options.TLSHTTP.Enabled || options.TLSGRPC.Enabled) && (grpcPort == httpPort) { + if (options.TLSHTTP.Enabled || options.TLSGRPC.Enabled) && !separatePorts { return nil, errors.New("server with TLS enabled can not use same host ports for gRPC and HTTP. Use dedicated HTTP and gRPC host ports instead") } - grpcServer, err := createGRPCServer(querySvc, metricsQuerySvc, options, tm, logger, tracer) + grpcServer, err := createGRPCServer(querySvc, metricsQuerySvc, options, tm, telset) if err != nil { return nil, err } - httpServer, err := createHTTPServer(querySvc, metricsQuerySvc, options, tm, tracer, logger) + httpServer, err := createHTTPServer(querySvc, metricsQuerySvc, options, tm, telset) if err != nil { return nil, err } return &Server{ - logger: logger, - healthCheck: healthCheck, querySvc: querySvc, queryOptions: options, - tracer: tracer, grpcServer: grpcServer, httpServer: httpServer, - separatePorts: grpcPort != httpPort, + separatePorts: separatePorts, + Setting: telset, }, nil } -func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.Manager, logger *zap.Logger, tracer *jtracer.JTracer) (*grpc.Server, error) { +func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.Manager, telset telemetery.Setting) (*grpc.Server, error) { var grpcOpts []grpc.ServerOption if options.TLSGRPC.Enabled { - tlsCfg, err := options.TLSGRPC.Config(logger) + tlsCfg, err := options.TLSGRPC.Config(telset.Logger) if err != nil { return nil, err } @@ -127,8 +128,7 @@ func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc. reflection.Register(server) handler := NewGRPCHandler(querySvc, metricsQuerySvc, GRPCHandlerOptions{ - Logger: logger, - Tracer: tracer, + Logger: telset.Logger, }) healthServer := health.NewServer() @@ -156,12 +156,11 @@ func createHTTPServer( metricsQuerySvc querysvc.MetricsQueryService, queryOpts *QueryOptions, tm *tenancy.Manager, - tracer *jtracer.JTracer, - logger *zap.Logger, + telset telemetery.Setting, ) (*httpServer, error) { apiHandlerOptions := []HandlerOption{ - HandlerOptions.Logger(logger), - HandlerOptions.Tracer(tracer), + HandlerOptions.Logger(telset.Logger), + HandlerOptions.Tracer(telset.TracerProvider), HandlerOptions.MetricsQueryService(metricsQuerySvc), } @@ -177,20 +176,20 @@ func createHTTPServer( (&apiv3.HTTPGateway{ QueryService: querySvc, TenancyMgr: tm, - Logger: logger, - Tracer: tracer, + Logger: telset.Logger, + Tracer: telset.TracerProvider, }).RegisterRoutes(r) apiHandler.RegisterRoutes(r) var handler http.Handler = r handler = additionalHeadersHandler(handler, queryOpts.AdditionalHeaders) if queryOpts.BearerTokenPropagation { - handler = bearertoken.PropagationHandler(logger, handler) + handler = bearertoken.PropagationHandler(telset.Logger, handler) } handler = handlers.CompressHandler(handler) - recoveryHandler := recoveryhandler.NewRecoveryHandler(logger, true) + recoveryHandler := recoveryhandler.NewRecoveryHandler(telset.Logger, true) - errorLog, _ := zap.NewStdLogAt(logger, zapcore.ErrorLevel) + errorLog, _ := zap.NewStdLogAt(telset.Logger, zapcore.ErrorLevel) server := &httpServer{ Server: &http.Server{ Handler: recoveryHandler(handler), @@ -200,14 +199,14 @@ func createHTTPServer( } if queryOpts.TLSHTTP.Enabled { - tlsCfg, err := queryOpts.TLSHTTP.Config(logger) // This checks if the certificates are correctly provided + tlsCfg, err := queryOpts.TLSHTTP.Config(telset.Logger) // This checks if the certificates are correctly provided if err != nil { return nil, err } server.TLSConfig = tlsCfg } - server.staticHandlerCloser = RegisterStaticHandler(r, logger, queryOpts, querySvc.GetCapabilities()) + server.staticHandlerCloser = RegisterStaticHandler(r, telset.Logger, queryOpts, querySvc.GetCapabilities()) return server, nil } @@ -232,10 +231,10 @@ func (s *Server) initListener() (cmux.CMux, error) { if err != nil { return nil, err } - s.logger.Info( + s.Logger.Info( "Query server started", - zap.String("http_addr", s.httpConn.Addr().String()), - zap.String("grpc_addr", s.grpcConn.Addr().String()), + zap.String("http_addr", s.HTTPAddr()), + zap.String("grpc_addr", s.GRPCAddr()), ) return nil, nil } @@ -253,7 +252,7 @@ func (s *Server) initListener() (cmux.CMux, error) { tcpPort = port } - s.logger.Info( + s.Logger.Info( "Query server started", zap.Int("port", tcpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) @@ -297,7 +296,8 @@ func (s *Server) Start() error { s.bgFinished.Add(1) go func() { - s.logger.Info("Starting HTTP server", zap.Int("port", httpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) + defer s.bgFinished.Done() + s.Logger.Info("Starting HTTP server", zap.Int("port", httpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) var err error if s.queryOptions.TLSHTTP.Enabled { err = s.httpServer.ServeTLS(s.httpConn, "", "") @@ -305,47 +305,56 @@ func (s *Server) Start() error { err = s.httpServer.Serve(s.httpConn) } if err != nil && !errors.Is(err, http.ErrServerClosed) && !errors.Is(err, cmux.ErrListenerClosed) && !errors.Is(err, cmux.ErrServerClosed) { - s.logger.Error("Could not start HTTP server", zap.Error(err)) + s.Logger.Error("Could not start HTTP server", zap.Error(err)) + s.ReportStatus(component.NewFatalErrorEvent(err)) + return } - s.logger.Info("HTTP server stopped", zap.Int("port", httpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) - s.healthCheck.Set(healthcheck.Unavailable) - s.bgFinished.Done() + s.Logger.Info("HTTP server stopped", zap.Int("port", httpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) }() // Start GRPC server concurrently s.bgFinished.Add(1) go func() { - s.logger.Info("Starting GRPC server", zap.Int("port", grpcPort), zap.String("addr", s.queryOptions.GRPCHostPort)) + defer s.bgFinished.Done() + s.Logger.Info("Starting GRPC server", zap.Int("port", grpcPort), zap.String("addr", s.queryOptions.GRPCHostPort)) err := s.grpcServer.Serve(s.grpcConn) if err != nil && !errors.Is(err, cmux.ErrListenerClosed) && !errors.Is(err, cmux.ErrServerClosed) { - s.logger.Error("Could not start GRPC server", zap.Error(err)) + s.Logger.Error("Could not start GRPC server", zap.Error(err)) + s.ReportStatus(component.NewFatalErrorEvent(err)) + return } - s.logger.Info("GRPC server stopped", zap.Int("port", grpcPort), zap.String("addr", s.queryOptions.GRPCHostPort)) - s.healthCheck.Set(healthcheck.Unavailable) - s.bgFinished.Done() + s.Logger.Info("GRPC server stopped", zap.Int("port", grpcPort), zap.String("addr", s.queryOptions.GRPCHostPort)) }() // Start cmux server concurrently. if !s.separatePorts { s.bgFinished.Add(1) go func() { - s.logger.Info("Starting CMUX server", zap.Int("port", tcpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) + defer s.bgFinished.Done() + s.Logger.Info("Starting CMUX server", zap.Int("port", tcpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) err := cmuxServer.Serve() // TODO: find a way to avoid string comparison. Even though cmux has ErrServerClosed, it's not returned here. if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - s.logger.Error("Could not start multiplexed server", zap.Error(err)) + s.Logger.Error("Could not start multiplexed server", zap.Error(err)) + s.ReportStatus(component.NewFatalErrorEvent(err)) + return } - s.logger.Info("CMUX server stopped", zap.Int("port", tcpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) - s.healthCheck.Set(healthcheck.Unavailable) - s.bgFinished.Done() + s.Logger.Info("CMUX server stopped", zap.Int("port", tcpPort), zap.String("addr", s.queryOptions.HTTPHostPort)) }() } - return nil } +func (s *Server) HTTPAddr() string { + return s.httpConn.Addr().String() +} + +func (s *Server) GRPCAddr() string { + return s.grpcConn.Addr().String() +} + // Close stops HTTP, GRPC servers and closes the port listener. func (s *Server) Close() error { errs := []error{ @@ -353,20 +362,20 @@ func (s *Server) Close() error { s.queryOptions.TLSHTTP.Close(), } - s.logger.Info("Closing HTTP server") + s.Logger.Info("Closing HTTP server") if err := s.httpServer.Close(); err != nil { errs = append(errs, fmt.Errorf("failed to close HTTP server: %w", err)) } - s.logger.Info("Stopping gRPC server") + s.Logger.Info("Stopping gRPC server") s.grpcServer.Stop() if !s.separatePorts { - s.logger.Info("Closing CMux server") + s.Logger.Info("Closing CMux server") s.cmuxServer.Close() } - s.bgFinished.Wait() - s.logger.Info("Server stopped") + + s.Logger.Info("Server stopped") return errors.Join(errs...) } diff --git a/cmd/query/app/server_test.go b/cmd/query/app/server_test.go index b556aecd87f..42504e37906 100644 --- a/cmd/query/app/server_test.go +++ b/cmd/query/app/server_test.go @@ -40,6 +40,7 @@ import ( "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/healthcheck" "github.com/jaegertracing/jaeger/pkg/jtracer" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/ports" "github.com/jaegertracing/jaeger/proto-gen/api_v2" @@ -49,6 +50,14 @@ import ( var testCertKeyLocation = "../../../pkg/config/tlscfg/testdata" +func initTelSet(logger *zap.Logger, tracerProvider *jtracer.JTracer, hc *healthcheck.HealthCheck) telemetery.Setting { + return telemetery.Setting{ + Logger: logger, + TracerProvider: tracerProvider.OTEL, + ReportStatus: telemetery.HCAdapter(hc), + } +} + func TestServerError(t *testing.T) { srv := &Server{ queryOptions: &QueryOptions{ @@ -66,10 +75,10 @@ func TestCreateTLSServerSinglePortError(t *testing.T) { KeyPath: testCertKeyLocation + "/example-server-key.pem", ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem", } - - _, err := NewServer(zaptest.NewLogger(t), healthcheck.New(), &querysvc.QueryService{}, nil, + telset := initTelSet(zaptest.NewLogger(t), jtracer.NoOp(), healthcheck.New()) + _, err := NewServer(&querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8080", TLSGRPC: tlsCfg, TLSHTTP: tlsCfg}, - tenancy.NewManager(&tenancy.Options{}), jtracer.NoOp()) + tenancy.NewManager(&tenancy.Options{}), telset) require.Error(t, err) } @@ -80,10 +89,10 @@ func TestCreateTLSGrpcServerError(t *testing.T) { KeyPath: "invalid/path", ClientCAPath: "invalid/path", } - - _, err := NewServer(zaptest.NewLogger(t), healthcheck.New(), &querysvc.QueryService{}, nil, + telset := initTelSet(zaptest.NewLogger(t), jtracer.NoOp(), healthcheck.New()) + _, err := NewServer(&querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8081", TLSGRPC: tlsCfg}, - tenancy.NewManager(&tenancy.Options{}), jtracer.NoOp()) + tenancy.NewManager(&tenancy.Options{}), telset) require.Error(t, err) } @@ -94,10 +103,10 @@ func TestCreateTLSHttpServerError(t *testing.T) { KeyPath: "invalid/path", ClientCAPath: "invalid/path", } - - _, err := NewServer(zaptest.NewLogger(t), healthcheck.New(), &querysvc.QueryService{}, nil, + telset := initTelSet(zaptest.NewLogger(t), jtracer.NoOp(), healthcheck.New()) + _, err := NewServer(&querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8081", TLSHTTP: tlsCfg}, - tenancy.NewManager(&tenancy.Options{}), jtracer.NoOp()) + tenancy.NewManager(&tenancy.Options{}), telset) require.Error(t, err) } @@ -344,8 +353,8 @@ func TestServerHTTPTLS(t *testing.T) { } serverOptions := &QueryOptions{ - GRPCHostPort: ports.GetAddressFromCLIOptions(ports.QueryGRPC, ""), - HTTPHostPort: ports.GetAddressFromCLIOptions(ports.QueryHTTP, ""), + GRPCHostPort: ":0", + HTTPHostPort: ":0", TLSHTTP: test.TLS, TLSGRPC: tlsGrpc, QueryOptionsBase: QueryOptionsBase{ @@ -354,11 +363,11 @@ func TestServerHTTPTLS(t *testing.T) { } flagsSvc := flags.NewService(ports.QueryAdminHTTP) flagsSvc.Logger = zaptest.NewLogger(t) - + telset := initTelSet(flagsSvc.Logger, jtracer.NoOp(), flagsSvc.HC()) querySvc := makeQuerySvc() - server, err := NewServer(flagsSvc.Logger, flagsSvc.HC(), querySvc.qs, + server, err := NewServer(querySvc.qs, nil, serverOptions, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp()) + telset) require.NoError(t, err) require.NoError(t, server.Start()) t.Cleanup(func() { @@ -376,14 +385,14 @@ func TestServerHTTPTLS(t *testing.T) { require.NoError(t, err0) dialer := &net.Dialer{Timeout: 2 * time.Second} - conn, err1 := tls.DialWithDialer(dialer, "tcp", "localhost:"+fmt.Sprintf("%d", ports.QueryHTTP), clientTLSCfg) + conn, err1 := tls.DialWithDialer(dialer, "tcp", server.HTTPAddr(), clientTLSCfg) clientError = err1 clientClose = nil if conn != nil { clientClose = conn.Close } } else { - conn, err1 := net.DialTimeout("tcp", "localhost:"+fmt.Sprintf("%d", ports.QueryHTTP), 2*time.Second) + conn, err1 := net.DialTimeout("tcp", server.HTTPAddr(), 2*time.Second) clientError = err1 clientClose = nil if conn != nil { @@ -408,7 +417,7 @@ func TestServerHTTPTLS(t *testing.T) { } querySvc.spanReader.On("FindTraces", mock.Anything, mock.Anything).Return([]*model.Trace{mockTrace}, nil).Once() queryString := "/api/traces?service=service&start=0&end=0&operation=operation&limit=200&minDuration=20ms" - req, err := http.NewRequest(http.MethodGet, "https://localhost:"+fmt.Sprintf("%d", ports.QueryHTTP)+queryString, nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://%s/%s", server.HTTPAddr(), queryString), nil) require.NoError(t, err) req.Header.Add("Accept", "application/json") @@ -481,8 +490,8 @@ func TestServerGRPCTLS(t *testing.T) { tlsHttp = enabledTLSCfg } serverOptions := &QueryOptions{ - GRPCHostPort: ports.GetAddressFromCLIOptions(ports.QueryGRPC, ""), - HTTPHostPort: ports.GetAddressFromCLIOptions(ports.QueryHTTP, ""), + GRPCHostPort: ":0", + HTTPHostPort: ":0", TLSHTTP: tlsHttp, TLSGRPC: test.TLS, QueryOptionsBase: QueryOptionsBase{ @@ -493,9 +502,10 @@ func TestServerGRPCTLS(t *testing.T) { flagsSvc.Logger = zaptest.NewLogger(t) querySvc := makeQuerySvc() - server, err := NewServer(flagsSvc.Logger, flagsSvc.HC(), querySvc.qs, + telset := initTelSet(flagsSvc.Logger, jtracer.NoOp(), flagsSvc.HC()) + server, err := NewServer(querySvc.qs, nil, serverOptions, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp()) + telset) require.NoError(t, err) require.NoError(t, server.Start()) t.Cleanup(func() { @@ -508,9 +518,9 @@ func TestServerGRPCTLS(t *testing.T) { require.NoError(t, err0) defer test.clientTLS.Close() creds := credentials.NewTLS(clientTLSCfg) - client = newGRPCClientWithTLS(t, ports.PortToHostPort(ports.QueryGRPC), creds) + client = newGRPCClientWithTLS(t, server.GRPCAddr(), creds) } else { - client = newGRPCClientWithTLS(t, ports.PortToHostPort(ports.QueryGRPC), nil) + client = newGRPCClientWithTLS(t, server.GRPCAddr(), nil) } t.Cleanup(func() { require.NoError(t, client.conn.Close()) @@ -535,7 +545,8 @@ func TestServerGRPCTLS(t *testing.T) { } func TestServerBadHostPort(t *testing.T) { - _, err := NewServer(zaptest.NewLogger(t), healthcheck.New(), &querysvc.QueryService{}, nil, + telset := initTelSet(zaptest.NewLogger(t), jtracer.NoOp(), healthcheck.New()) + _, err := NewServer(&querysvc.QueryService{}, nil, &QueryOptions{ HTTPHostPort: "8080", // bad string, not :port GRPCHostPort: "127.0.0.1:8081", @@ -544,10 +555,10 @@ func TestServerBadHostPort(t *testing.T) { }, }, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp()) + telset) require.Error(t, err) - _, err = NewServer(zaptest.NewLogger(t), healthcheck.New(), &querysvc.QueryService{}, nil, + _, err = NewServer(&querysvc.QueryService{}, nil, &QueryOptions{ HTTPHostPort: "127.0.0.1:8081", GRPCHostPort: "9123", // bad string, not :port @@ -556,7 +567,7 @@ func TestServerBadHostPort(t *testing.T) { }, }, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp()) + telset) require.Error(t, err) } @@ -566,7 +577,7 @@ func TestServerInUseHostPort(t *testing.T) { conn, err := net.Listen("tcp", availableHostPort) require.NoError(t, err) defer func() { require.NoError(t, conn.Close()) }() - + telset := initTelSet(zaptest.NewLogger(t), jtracer.NoOp(), healthcheck.New()) testCases := []struct { name string httpHostPort string @@ -578,8 +589,6 @@ func TestServerInUseHostPort(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { server, err := NewServer( - zaptest.NewLogger(t), - healthcheck.New(), &querysvc.QueryService{}, nil, &QueryOptions{ @@ -590,7 +599,7 @@ func TestServerInUseHostPort(t *testing.T) { }, }, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp(), + telset, ) require.NoError(t, err) require.Error(t, server.Start()) @@ -602,9 +611,10 @@ func TestServerInUseHostPort(t *testing.T) { func TestServerSinglePort(t *testing.T) { flagsSvc := flags.NewService(ports.QueryAdminHTTP) flagsSvc.Logger = zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller())) - hostPort := ports.GetAddressFromCLIOptions(ports.QueryHTTP, "") + hostPort := ports.PortToHostPort(ports.QueryHTTP) querySvc := makeQuerySvc() - server, err := NewServer(flagsSvc.Logger, flagsSvc.HC(), querySvc.qs, nil, + telset := initTelSet(flagsSvc.Logger, jtracer.NoOp(), flagsSvc.HC()) + server, err := NewServer(querySvc.qs, nil, &QueryOptions{ GRPCHostPort: hostPort, HTTPHostPort: hostPort, @@ -613,7 +623,7 @@ func TestServerSinglePort(t *testing.T) { }, }, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp()) + telset) require.NoError(t, err) require.NoError(t, server.Start()) t.Cleanup(func() { @@ -644,9 +654,10 @@ func TestServerGracefulExit(t *testing.T) { hostPort := ports.PortToHostPort(ports.QueryAdminHTTP) querySvc := makeQuerySvc() - server, err := NewServer(flagsSvc.Logger, flagsSvc.HC(), querySvc.qs, nil, + telset := initTelSet(flagsSvc.Logger, jtracer.NoOp(), flagsSvc.HC()) + server, err := NewServer(querySvc.qs, nil, &QueryOptions{GRPCHostPort: hostPort, HTTPHostPort: hostPort}, - tenancy.NewManager(&tenancy.Options{}), jtracer.NoOp()) + tenancy.NewManager(&tenancy.Options{}), telset) require.NoError(t, err) require.NoError(t, server.Start()) @@ -676,11 +687,11 @@ func TestServerHandlesPortZero(t *testing.T) { flagsSvc.Logger = zap.New(zapCore) querySvc := &querysvc.QueryService{} - tracer := jtracer.NoOp() - server, err := NewServer(flagsSvc.Logger, flagsSvc.HC(), querySvc, nil, + telset := initTelSet(flagsSvc.Logger, jtracer.NoOp(), flagsSvc.HC()) + server, err := NewServer(querySvc, nil, &QueryOptions{GRPCHostPort: ":0", HTTPHostPort: ":0"}, tenancy.NewManager(&tenancy.Options{}), - tracer) + telset) require.NoError(t, err) require.NoError(t, server.Start()) defer server.Close() @@ -688,13 +699,8 @@ func TestServerHandlesPortZero(t *testing.T) { message := logs.FilterMessage("Query server started") assert.Equal(t, 1, message.Len(), "Expected 'Query server started' log message.") - onlyEntry := message.All()[0] - port := onlyEntry.ContextMap()["port"].(int64) - assert.Greater(t, port, int64(0)) - grpctest.ReflectionServiceValidator{ - HostPort: fmt.Sprintf(":%v", port), - Server: server.grpcServer, + HostPort: server.GRPCAddr(), ExpectedServices: []string{ "jaeger.api_v2.QueryService", "jaeger.api_v3.QueryService", @@ -735,8 +741,9 @@ func TestServerHTTPTenancy(t *testing.T) { tenancyMgr := tenancy.NewManager(&serverOptions.Tenancy) querySvc := makeQuerySvc() querySvc.spanReader.On("FindTraces", mock.Anything, mock.Anything).Return([]*model.Trace{mockTrace}, nil).Once() - server, err := NewServer(zaptest.NewLogger(t), healthcheck.New(), querySvc.qs, - nil, serverOptions, tenancyMgr, jtracer.NoOp()) + telset := initTelSet(zaptest.NewLogger(t), jtracer.NoOp(), healthcheck.New()) + server, err := NewServer(querySvc.qs, + nil, serverOptions, tenancyMgr, telset) require.NoError(t, err) require.NoError(t, server.Start()) t.Cleanup(func() { diff --git a/cmd/query/app/token_propagation_test.go b/cmd/query/app/token_propagation_test.go index 1e9a0ebfea4..a474c1188cc 100644 --- a/cmd/query/app/token_propagation_test.go +++ b/cmd/query/app/token_propagation_test.go @@ -32,6 +32,7 @@ import ( "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/jtracer" "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/plugin/storage/es" "github.com/jaegertracing/jaeger/ports" @@ -91,7 +92,12 @@ func runQueryService(t *testing.T, esURL string) *Server { require.NoError(t, err) querySvc := querysvc.NewQueryService(spanReader, nil, querysvc.QueryServiceOptions{}) - server, err := NewServer(flagsSvc.Logger, flagsSvc.HC(), querySvc, nil, + telset := telemetery.Setting{ + Logger: flagsSvc.Logger, + TracerProvider: jtracer.NoOp().OTEL, + ReportStatus: telemetery.HCAdapter(flagsSvc.HC()), + } + server, err := NewServer(querySvc, nil, &QueryOptions{ GRPCHostPort: ":0", HTTPHostPort: ":0", @@ -100,7 +106,7 @@ func runQueryService(t *testing.T, esURL string) *Server { }, }, tenancy.NewManager(&tenancy.Options{}), - jtracer.NoOp(), + telset, ) require.NoError(t, err) require.NoError(t, server.Start()) diff --git a/cmd/query/main.go b/cmd/query/main.go index 42f698669f0..36c54c63197 100644 --- a/cmd/query/main.go +++ b/cmd/query/main.go @@ -37,6 +37,7 @@ import ( "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/jtracer" "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/pkg/version" metricsPlugin "github.com/jaegertracing/jaeger/plugin/metrics" @@ -113,7 +114,12 @@ func main() { dependencyReader, *queryServiceOptions) tm := tenancy.NewManager(&queryOpts.Tenancy) - server, err := app.NewServer(svc.Logger, svc.HC(), queryService, metricsQueryService, queryOpts, tm, jt) + telset := telemetery.Setting{ + Logger: logger, + TracerProvider: jt.OTEL, + ReportStatus: telemetery.HCAdapter(svc.HC()), + } + server, err := app.NewServer(queryService, metricsQueryService, queryOpts, tm, telset) if err != nil { logger.Fatal("Failed to create server", zap.Error(err)) } diff --git a/cmd/remote-storage/app/server.go b/cmd/remote-storage/app/server.go index a79187ca471..58b72445720 100644 --- a/cmd/remote-storage/app/server.go +++ b/cmd/remote-storage/app/server.go @@ -19,6 +19,7 @@ import ( "net" "sync" + "go.opentelemetry.io/collector/component" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -26,7 +27,7 @@ import ( "google.golang.org/grpc/reflection" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" - "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" "github.com/jaegertracing/jaeger/storage" @@ -36,32 +37,30 @@ import ( // Server runs a gRPC server type Server struct { - logger *zap.Logger - healthcheck *healthcheck.HealthCheck - opts *Options + opts *Options grpcConn net.Listener grpcServer *grpc.Server wg sync.WaitGroup + telemetery.Setting } // NewServer creates and initializes Server. -func NewServer(options *Options, storageFactory storage.Factory, tm *tenancy.Manager, logger *zap.Logger, healthcheck *healthcheck.HealthCheck) (*Server, error) { - handler, err := createGRPCHandler(storageFactory, logger) +func NewServer(options *Options, storageFactory storage.Factory, tm *tenancy.Manager, telset telemetery.Setting) (*Server, error) { + handler, err := createGRPCHandler(storageFactory, telset.Logger) if err != nil { return nil, err } - grpcServer, err := createGRPCServer(options, tm, handler, logger) + grpcServer, err := createGRPCServer(options, tm, handler, telset.Logger) if err != nil { return nil, err } return &Server{ - logger: logger, - healthcheck: healthcheck, - opts: options, - grpcServer: grpcServer, + opts: options, + grpcServer: grpcServer, + Setting: telset, }, nil } @@ -129,15 +128,15 @@ func (s *Server) Start() error { if err != nil { return err } - s.logger.Info("Starting GRPC server", zap.Stringer("addr", listener.Addr())) + s.Logger.Info("Starting GRPC server", zap.Stringer("addr", listener.Addr())) s.grpcConn = listener s.wg.Add(1) go func() { defer s.wg.Done() if err := s.grpcServer.Serve(s.grpcConn); err != nil { - s.logger.Error("GRPC server exited", zap.Error(err)) + s.Logger.Error("GRPC server exited", zap.Error(err)) + s.ReportStatus(component.NewFatalErrorEvent(err)) } - s.healthcheck.Set(healthcheck.Unavailable) }() return nil @@ -149,5 +148,6 @@ func (s *Server) Close() error { s.grpcConn.Close() s.opts.TLSGRPC.Close() s.wg.Wait() + s.ReportStatus(component.NewStatusEvent(component.StatusStopped)) return nil } diff --git a/cmd/remote-storage/app/server_test.go b/cmd/remote-storage/app/server_test.go index 29324de7d05..debad3dff51 100644 --- a/cmd/remote-storage/app/server_test.go +++ b/cmd/remote-storage/app/server_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" "google.golang.org/grpc" @@ -33,6 +34,7 @@ import ( "github.com/jaegertracing/jaeger/internal/grpctest" "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/ports" "github.com/jaegertracing/jaeger/proto-gen/storage_v1" @@ -51,14 +53,16 @@ func TestNewServer_CreateStorageErrors(t *testing.T) { factory.On("CreateSpanWriter").Return(nil, nil) factory.On("CreateDependencyReader").Return(nil, errors.New("no deps")).Once() factory.On("CreateDependencyReader").Return(nil, nil) - + telset := telemetery.Setting{ + Logger: zap.NewNop(), + ReportStatus: func(*component.StatusEvent) {}, + } f := func() (*Server, error) { return NewServer( &Options{GRPCHostPort: ":0"}, factory, tenancy.NewManager(&tenancy.Options{}), - zap.NewNop(), - healthcheck.New(), + telset, ) } _, err := f() @@ -77,7 +81,7 @@ func TestNewServer_CreateStorageErrors(t *testing.T) { require.NoError(t, err) err = s.Start() require.NoError(t, err) - validateGRPCServer(t, s.grpcConn.Addr().String(), s.grpcServer) + validateGRPCServer(t, s.grpcConn.Addr().String()) s.grpcConn.Close() // causes logged error } @@ -123,13 +127,16 @@ func TestNewServer_TLSConfigError(t *testing.T) { KeyPath: "invalid/path", ClientCAPath: "invalid/path", } + telset := telemetery.Setting{ + Logger: zap.NewNop(), + ReportStatus: telemetery.HCAdapter(healthcheck.New()), + } storageMocks := newStorageMocks() _, err := NewServer( &Options{GRPCHostPort: ":8081", TLSGRPC: tlsCfg}, storageMocks.factory, tenancy.NewManager(&tenancy.Options{}), - zap.NewNop(), - healthcheck.New(), + telset, ) require.Error(t, err) assert.Contains(t, err.Error(), "invalid TLS config") @@ -334,12 +341,15 @@ func TestServerGRPCTLS(t *testing.T) { storageMocks.reader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(expectedServices, nil) tm := tenancy.NewManager(&tenancy.Options{Enabled: true}) + telset := telemetery.Setting{ + Logger: flagsSvc.Logger, + ReportStatus: telemetery.HCAdapter(flagsSvc.HC()), + } server, err := NewServer( serverOptions, storageMocks.factory, tm, - flagsSvc.Logger, - flagsSvc.HC(), + telset, ) require.NoError(t, err) require.NoError(t, server.Start()) @@ -380,13 +390,15 @@ func TestServerHandlesPortZero(t *testing.T) { zapCore, logs := observer.New(zap.InfoLevel) flagsSvc.Logger = zap.New(zapCore) storageMocks := newStorageMocks() - + telset := telemetery.Setting{ + Logger: flagsSvc.Logger, + ReportStatus: telemetery.HCAdapter(flagsSvc.HC()), + } server, err := NewServer( &Options{GRPCHostPort: ":0"}, storageMocks.factory, tenancy.NewManager(&tenancy.Options{}), - flagsSvc.Logger, - flagsSvc.HC(), + telset, ) require.NoError(t, err) @@ -398,17 +410,16 @@ func TestServerHandlesPortZero(t *testing.T) { onlyEntry := message.All()[0] hostPort := onlyEntry.ContextMap()["addr"].(string) - validateGRPCServer(t, hostPort, server.grpcServer) + validateGRPCServer(t, hostPort) server.Close() assert.Equal(t, healthcheck.Unavailable, flagsSvc.HC().Get()) } -func validateGRPCServer(t *testing.T, hostPort string, server *grpc.Server) { +func validateGRPCServer(t *testing.T, hostPort string) { grpctest.ReflectionServiceValidator{ HostPort: hostPort, - Server: server, ExpectedServices: []string{ "jaeger.storage.v1.SpanReaderPlugin", "jaeger.storage.v1.SpanWriterPlugin", @@ -417,7 +428,7 @@ func validateGRPCServer(t *testing.T, hostPort string, server *grpc.Server) { "jaeger.storage.v1.ArchiveSpanReaderPlugin", "jaeger.storage.v1.ArchiveSpanWriterPlugin", "jaeger.storage.v1.StreamingSpanWriterPlugin", - // "grpc.health.v1.Health", + "grpc.health.v1.Health", }, }.Execute(t) } diff --git a/cmd/remote-storage/main.go b/cmd/remote-storage/main.go index f194b491e6e..a810deab86e 100644 --- a/cmd/remote-storage/main.go +++ b/cmd/remote-storage/main.go @@ -32,6 +32,7 @@ import ( "github.com/jaegertracing/jaeger/cmd/remote-storage/app" "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/pkg/version" "github.com/jaegertracing/jaeger/plugin/storage" @@ -77,7 +78,11 @@ func main() { } tm := tenancy.NewManager(&opts.Tenancy) - server, err := app.NewServer(opts, storageFactory, tm, svc.Logger, svc.HC()) + telset := telemetery.Setting{ + Logger: svc.Logger, + ReportStatus: telemetery.HCAdapter(svc.HC()), + } + server, err := app.NewServer(opts, storageFactory, tm, telset) if err != nil { logger.Fatal("Failed to create server", zap.Error(err)) } diff --git a/cmd/tracegen/main.go b/cmd/tracegen/main.go index 30b70411078..9970c619128 100644 --- a/cmd/tracegen/main.go +++ b/cmd/tracegen/main.go @@ -19,8 +19,10 @@ import ( "errors" "flag" "fmt" + "time" "github.com/go-logr/zapr" + "go.opentelemetry.io/contrib/samplers/jaegerremote" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" @@ -39,6 +41,8 @@ import ( "github.com/jaegertracing/jaeger/pkg/version" ) +var flagAdaptiveSamplingEndpoint string + func main() { zc := zap.NewDevelopmentConfig() zc.Level = zap.NewAtomicLevelAt(zapcore.Level(-8)) // level used by OTEL's Debug() @@ -51,6 +55,14 @@ func main() { fs := flag.CommandLine cfg := new(tracegen.Config) cfg.Flags(fs) + fs.StringVar( + &flagAdaptiveSamplingEndpoint, + "adaptive-sampling", + "", + "HTTP endpoint to use to retrieve sampling strategies, "+ + "e.g. http://localhost:14268/api/sampling. "+ + "When not specified a standard SDK sampler will be used "+ + "(see OTEL_TRACES_SAMPLER env var in OTEL docs)") flag.Parse() logger.Info(version.Get().String()) @@ -94,10 +106,21 @@ func createTracers(cfg *tracegen.Config, logger *zap.Logger) ([]trace.Tracer, fu logger.Sugar().Fatalf("resource creation failed: %s", err) } - tp := sdktrace.NewTracerProvider( + opts := []sdktrace.TracerProviderOption{ sdktrace.WithBatcher(exp, sdktrace.WithBlocking()), sdktrace.WithResource(res), - ) + } + if flagAdaptiveSamplingEndpoint != "" { + jaegerRemoteSampler := jaegerremote.New( + svc, + jaegerremote.WithSamplingServerURL(flagAdaptiveSamplingEndpoint), + jaegerremote.WithSamplingRefreshInterval(5*time.Second), + jaegerremote.WithInitialSampler(sdktrace.TraceIDRatioBased(0.5)), + ) + opts = append(opts, sdktrace.WithSampler(jaegerRemoteSampler)) + logger.Sugar().Infof("using adaptive sampling URL: %s", flagAdaptiveSamplingEndpoint) + } + tp := sdktrace.NewTracerProvider(opts...) tracers = append(tracers, tp.Tracer(cfg.Service)) shutdown = append(shutdown, tp.Shutdown) } diff --git a/docker-compose/elasticsearch/v6/docker-compose.yml b/docker-compose/elasticsearch/v6/docker-compose.yml new file mode 100644 index 00000000000..fdce40870e9 --- /dev/null +++ b/docker-compose/elasticsearch/v6/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3.8' + +services: + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.23 + environment: + - discovery.type=single-node + - http.host=0.0.0.0 + - transport.host=127.0.0.1 + ports: + - "9200:9200" \ No newline at end of file diff --git a/docker-compose/monitor/README.md b/docker-compose/monitor/README.md index 311fc10ca75..df8c30adb36 100644 --- a/docker-compose/monitor/README.md +++ b/docker-compose/monitor/README.md @@ -24,6 +24,45 @@ The following diagram illustrates the relationship between these components: ![SPM diagram](./diagram.png) +```mermaid +flowchart LR + SDK -->|traces| Receiver + Receiver --> MG + Receiver --> Batch + MG --> ExpMetrics + Batch --> ExpTraces + ExpMetrics -->|metrics| Prometheus[(Prometheus)] + ExpTraces -->|traces| Jaeger[Jaeger + Collector] + Prometheus -.-> JaegerUI + Jaeger --> Storage[(Storage)] + Storage -.-> JaegerUI[Jaeger + Query + and UI] + + style Prometheus fill:red,color:white + style Jaeger fill:blue,color:white + style JaegerUI fill:blue,color:white + style Storage fill:gray,color:white + + subgraph Application + SDK[OTel + SDK] + end + + subgraph OTEL[OTel Collector] + Receiver + Batch + MG[Span + Metrics + Connector] + ExpTraces[Traces + Exporter] + ExpMetrics[Metrics + Exporter] + end +``` + # Getting Started ## Quickstart @@ -160,7 +199,7 @@ quantile = 'quantile=' floatValue - The quantile to compute the latency 'P' value. Valid range (0,1]. - Mandatory for 'latencies' type. -groupByOperation = 'groupByOperation=' boolValue +groupByOperation = 'groupByOperation=' boolValue boolValue = '1' | 't' | 'T' | 'true' | 'TRUE' | 'True' | 0 | 'f' | 'F' | 'false' | 'FALSE' | 'False' - A boolean value which will determine if the metrics query will also group by operation. - Optional with default: false diff --git a/docker-compose/monitor/docker-compose.yml b/docker-compose/monitor/docker-compose.yml index cc98b4da785..61e9170148c 100644 --- a/docker-compose/monitor/docker-compose.yml +++ b/docker-compose/monitor/docker-compose.yml @@ -29,8 +29,11 @@ services: microsim: networks: - backend - image: yurishkuro/microsim:0.3.0 - command: "-j http://otel_collector:14278/api/traces -d 24h -s 500ms" + image: yurishkuro/microsim:v0.4.1 + command: "-d 24h -s 500ms" + environment: + - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel_collector:4318 + - OTEL_EXPORTER_OTLP_INSECURE=true depends_on: - otel_collector prometheus: diff --git a/examples/hotrod/cmd/root.go b/examples/hotrod/cmd/root.go index 7db0b0bda83..1a6a0d22211 100644 --- a/examples/hotrod/cmd/root.go +++ b/examples/hotrod/cmd/root.go @@ -56,8 +56,6 @@ func init() { // onInitialize is called before the command is executed. func onInitialize() { - jaegerclientenv2otel.MapJaegerToOtelEnvVars(logger) - zapOptions := []zap.Option{ zap.AddStacktrace(zapcore.FatalLevel), zap.AddCallerSkip(1), @@ -68,6 +66,9 @@ func onInitialize() { ) } logger, _ = zap.NewDevelopment(zapOptions...) + + jaegerclientenv2otel.MapJaegerToOtelEnvVars(logger) + metricsFactory = prometheus.New().Namespace(metrics.NSOptions{Name: "hotrod", Tags: nil}) if config.MySQLGetDelay != fixDBConnDelay { diff --git a/examples/hotrod/docker-compose.yml b/examples/hotrod/docker-compose.yml index db58cb2860d..048de7de59e 100644 --- a/examples/hotrod/docker-compose.yml +++ b/examples/hotrod/docker-compose.yml @@ -1,20 +1,21 @@ version: '3.7' - # To run a specific version of Jaeger, use environment variable, e.g.: # JAEGER_VERSION=1.52 docker compose up services: jaeger: - image: jaegertracing/all-in-one:${JAEGER_VERSION:-latest} + image: ${REGISTRY:-}jaegertracing/all-in-one:${JAEGER_VERSION:-latest} ports: - "16686:16686" + - "4317:4317" - "4318:4318" environment: - LOG_LEVEL=debug networks: - jaeger-example + hotrod: - image: jaegertracing/example-hotrod:${JAEGER_VERSION:-latest} + image: ${REGISTRY:-}jaegertracing/example-hotrod:${JAEGER_VERSION:-latest} # To run the latest trunk build, find the tag at Docker Hub and use the line below # https://hub.docker.com/r/jaegertracing/example-hotrod-snapshot/tags #image: jaegertracing/example-hotrod-snapshot:0ab8f2fcb12ff0d10830c1ee3bb52b745522db6c diff --git a/go.mod b/go.mod index 7740259e468..cf46248a6d2 100644 --- a/go.mod +++ b/go.mod @@ -23,12 +23,13 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/kr/pretty v0.3.1 github.com/olivere/elastic v6.2.37+incompatible - github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.104.0 - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.104.0 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.104.0 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.104.0 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.104.0 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.104.0 + github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.105.0 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.105.0 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.105.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.105.0 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.105.0 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.105.0 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.105.0 github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.55.0 @@ -39,37 +40,38 @@ require ( github.com/stretchr/testify v1.9.0 github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/xdg-go/scram v1.1.2 - go.opentelemetry.io/collector/component v0.104.0 - go.opentelemetry.io/collector/config/configauth v0.104.0 - go.opentelemetry.io/collector/config/configgrpc v0.104.0 - go.opentelemetry.io/collector/config/confighttp v0.104.0 - go.opentelemetry.io/collector/config/configretry v1.11.0 - go.opentelemetry.io/collector/config/configtls v0.104.0 - go.opentelemetry.io/collector/confmap v0.104.0 - go.opentelemetry.io/collector/confmap/converter/expandconverter v0.104.0 - go.opentelemetry.io/collector/confmap/provider/envprovider v0.104.0 - go.opentelemetry.io/collector/confmap/provider/fileprovider v0.104.0 - go.opentelemetry.io/collector/confmap/provider/httpprovider v0.104.0 - go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.104.0 - go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.104.0 - go.opentelemetry.io/collector/connector v0.104.0 - go.opentelemetry.io/collector/connector/forwardconnector v0.104.0 - go.opentelemetry.io/collector/consumer v0.104.0 - go.opentelemetry.io/collector/exporter v0.104.0 - go.opentelemetry.io/collector/exporter/otlpexporter v0.104.0 - go.opentelemetry.io/collector/exporter/otlphttpexporter v0.104.0 - go.opentelemetry.io/collector/extension v0.104.0 - go.opentelemetry.io/collector/extension/ballastextension v0.104.0 - go.opentelemetry.io/collector/extension/zpagesextension v0.104.0 - go.opentelemetry.io/collector/otelcol v0.104.0 - go.opentelemetry.io/collector/pdata v1.11.0 - go.opentelemetry.io/collector/processor v0.104.0 - go.opentelemetry.io/collector/processor/batchprocessor v0.104.0 - go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.104.0 - go.opentelemetry.io/collector/receiver v0.104.0 - go.opentelemetry.io/collector/receiver/otlpreceiver v0.104.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 + go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/config/configauth v0.105.0 + go.opentelemetry.io/collector/config/configgrpc v0.105.0 + go.opentelemetry.io/collector/config/confighttp v0.105.0 + go.opentelemetry.io/collector/config/configretry v1.12.0 + go.opentelemetry.io/collector/config/configtls v1.12.0 + go.opentelemetry.io/collector/confmap v0.105.0 + go.opentelemetry.io/collector/confmap/converter/expandconverter v0.105.0 + go.opentelemetry.io/collector/confmap/provider/envprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/fileprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/httpprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.105.0 + go.opentelemetry.io/collector/connector v0.105.0 + go.opentelemetry.io/collector/connector/forwardconnector v0.105.0 + go.opentelemetry.io/collector/consumer v0.105.0 + go.opentelemetry.io/collector/exporter v0.105.0 + go.opentelemetry.io/collector/exporter/otlpexporter v0.105.0 + go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0 + go.opentelemetry.io/collector/extension v0.105.0 + go.opentelemetry.io/collector/extension/ballastextension v0.105.0 + go.opentelemetry.io/collector/extension/zpagesextension v0.105.0 + go.opentelemetry.io/collector/otelcol v0.105.0 + go.opentelemetry.io/collector/pdata v1.12.0 + go.opentelemetry.io/collector/processor v0.105.0 + go.opentelemetry.io/collector/processor/batchprocessor v0.105.0 + go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.105.0 + go.opentelemetry.io/collector/receiver v0.105.0 + go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 + go.opentelemetry.io/contrib/samplers/jaegerremote v0.22.0 go.opentelemetry.io/otel v1.28.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 @@ -83,13 +85,22 @@ require ( go.uber.org/automaxprocs v1.5.3 go.uber.org/goleak v1.3.0 go.uber.org/zap v1.27.0 - golang.org/x/net v0.26.0 - golang.org/x/sys v0.21.0 + golang.org/x/net v0.27.0 + golang.org/x/sys v0.22.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 ) +require ( + go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.105.0 // indirect + go.opentelemetry.io/collector/pdata/testdata v0.105.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 // indirect + go.opentelemetry.io/otel/log v0.4.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.4.0 // indirect +) + require ( github.com/IBM/sarama v1.43.2 // indirect github.com/aws/aws-sdk-go v1.53.11 // indirect @@ -150,14 +161,13 @@ require ( github.com/mostynb/go-grpc-compression v1.2.3 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.104.0 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.104.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.104.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.104.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.104.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.104.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure v0.104.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.104.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.105.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.105.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.105.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.105.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.105.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure v0.105.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.105.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect @@ -170,11 +180,11 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/relvacode/iso8601 v1.4.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/rs/cors v1.10.1 // indirect + github.com/rs/cors v1.11.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/shirou/gopsutil/v4 v4.24.5 // indirect + github.com/shirou/gopsutil/v4 v4.24.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect @@ -189,28 +199,28 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector v0.104.0 // indirect - go.opentelemetry.io/collector/config/configcompression v1.11.0 // indirect - go.opentelemetry.io/collector/config/confignet v0.104.0 // indirect - go.opentelemetry.io/collector/config/configopaque v1.11.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.104.0 // indirect - go.opentelemetry.io/collector/config/internal v0.104.0 // indirect - go.opentelemetry.io/collector/exporter/debugexporter v0.104.0 - go.opentelemetry.io/collector/extension/auth v0.104.0 // indirect - go.opentelemetry.io/collector/featuregate v1.11.0 // indirect - go.opentelemetry.io/collector/semconv v0.104.0 // indirect - go.opentelemetry.io/collector/service v0.104.0 // indirect - go.opentelemetry.io/contrib/config v0.7.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.27.0 // indirect - go.opentelemetry.io/contrib/zpages v0.52.0 // indirect - go.opentelemetry.io/otel/bridge/opencensus v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 // indirect + go.opentelemetry.io/collector v0.105.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.12.0 // indirect + go.opentelemetry.io/collector/config/confignet v0.105.0 + go.opentelemetry.io/collector/config/configopaque v1.12.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect + go.opentelemetry.io/collector/config/internal v0.105.0 // indirect + go.opentelemetry.io/collector/exporter/debugexporter v0.105.0 + go.opentelemetry.io/collector/extension/auth v0.105.0 // indirect + go.opentelemetry.io/collector/featuregate v1.12.0 // indirect + go.opentelemetry.io/collector/semconv v0.105.0 // indirect + go.opentelemetry.io/collector/service v0.105.0 // indirect + go.opentelemetry.io/contrib/config v0.8.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.28.0 // indirect + go.opentelemetry.io/contrib/zpages v0.53.0 // indirect + go.opentelemetry.io/otel/bridge/opencensus v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/text v0.16.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect diff --git a/go.sum b/go.sum index ae3a62df84b..6128a76429a 100644 --- a/go.sum +++ b/go.sum @@ -254,36 +254,36 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.104.0 h1:6dvpPt8pCcV+TfMnnanFk2NQYf9HN1voSS9iIHdW+L8= -github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.104.0/go.mod h1:MfSM6mt9qH3vHCaj2rlX6IY/7fN+zCLzNJC25XG9rNU= -github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.104.0 h1:xDA/V4cZ4LXj70la1uzvGaX/xfzeGIEq3txhF7W6TAg= -github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.104.0/go.mod h1:BxDMJl5xzSbCwcuVFs9jIrBwc1sflBVdihNJxcEKkOE= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.104.0 h1:VD2R4q2XziQCJIAsQG2qhlSKyDoT9XaTR2LNkJHs2C0= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.104.0/go.mod h1:sOdPmcOeSXXqZeBflu3Oa+0aWFL8QkAKpr/X1txJYSs= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.104.0 h1:4ke4j/y7AQnRAyYveB+KGcdjVYEKVrwTxc3BDHagdd0= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.104.0/go.mod h1:I2zX9YBggIum9LAHXN1DqqbYOENrHXbXdkXouhwVCHw= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.104.0 h1:/koTWTWCFF7tBYkDX5UzCaEc/ceTU8jij/Yzuj0So3M= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.104.0/go.mod h1:KWVekIHTPScOrLKVYOiijxfEdGK5OBhD4EFNBh96ESg= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.104.0 h1:BGKkg/wBfDeKSMqj8tMC6v03XtMuL9lNltfZiMViBAU= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.104.0/go.mod h1:SqzW5R0kOIs6HAk18+zH+Ej7CehtLVKtyBifT1wqAt0= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.104.0 h1:pH5vUaC85j8DcgfgFXlQW3QRoED9DCAGJ5lGw4J/UvE= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.104.0/go.mod h1:BsappX8t6TNyk+M0UZ3xMwEtn2IIHy7hqXlThdBoKjI= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.104.0 h1:bgS1X1UnUuYfKXsQPq3U50jsMNpN5lI+BcDeklPJOW8= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.104.0/go.mod h1:tImy4FWNu1qpaXRVaNi2BU+TmZHtYgLO6LbB6mspZio= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.104.0 h1:Pl4rXXpRG/xJuNWUS3I/w1jViHcrssMf47bGX/Ug/KY= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.104.0/go.mod h1:tP4dyc5+g/qoXYb8lmNj+y+Nhphn4MkL23/np0Zhx2g= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure v0.104.0 h1:V58XfkZBVuOcNL/+US/ZnG8mVy5AbVHV49mGDIgIMo8= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure v0.104.0/go.mod h1:HpEqZW6TOsujFvvOSkcCFj7N2NEW4LP/Q6X7/ryfSnA= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.104.0 h1:3joQv7QiLvsVfrpqYAxHvq3bKUaUEpgg93fMLW+TU2w= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.104.0/go.mod h1:aUAgxXQQPiSajwMXQv4LobDTc16ezeF9S1Xh53yHbOg= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.104.0 h1:oIYYjBh2kNi7APSArA/9YCQzLwOlMdUhqMxUoMoTChY= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.104.0/go.mod h1:hRayDDoU1e1POXuDO7RpwcqIirFJoCdSgHgICY0hNNI= -github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.104.0 h1:9HJ3ejNoiMFWxTRy9gobdurEocf79QlxwlYrOY9tMIQ= -github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.104.0/go.mod h1:Ax4DroNn/xKyjWoJCd3FQE9xOZqHSTdDEj1I3HLNOeQ= -github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.104.0 h1:sE+B+i3m9sMecnJZkljnUrykzkRkEFvXJWPckwbQOVc= -github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.104.0/go.mod h1:cyzp/19NsVmEz7mTS/LHf2m8e26MnlGK8x1kga3rX9I= -github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.104.0 h1:U04Ezl3Keb1j6bcVktvgvAbbMEyPDkM5sNboQgPYI1w= -github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.104.0/go.mod h1:GbpsurP8UERCcHyIB/gUMKcAK3kIypKGE0O+aqbNa/c= +github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.105.0 h1:DVaUWxeO8VNH/zQrP9vqz8eZwaaVzWQNCmcWGxbFoWk= +github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.105.0/go.mod h1:45iX3DRsbK2XlbHzEWtIvud56FE2XdC7crKIne4DUaU= +github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.105.0 h1:HcFHS9xNt2nI9yphhvvgWekr9zsP2jVi8IQUckAxCUk= +github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.105.0/go.mod h1:DB9+mDKgg/nO+vZca48EWvCTcOjHyjOeJKAbP+WfPXU= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.105.0 h1:HmimWATFFNI8o6n52DXTS2EjFRa6aETNqmA3MBGAxSI= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.105.0/go.mod h1:DoCIQkjzSFD/rRq9rsI4kzYKcHRn6B7g7txILM18dHQ= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.105.0 h1:kHHL4A9wL6TxM2sIUEXUpFXGPxrW7u002FJK+majI0s= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.105.0/go.mod h1:I+fL3494NrarNgHHs2D7kz0v02KGCmwmp3L89KUqENA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.105.0 h1:k89VhlOwi0uYkEOgoR0ISTct07GaBoQmhnI1yypKa4I= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.105.0/go.mod h1:6k6afq9nYwfBbo9z0XhS1AJzFtosTFHoBVJ5v/Ugv+8= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.105.0 h1:xQfrUHMnTnXolFjIaMAoORoLRBl7/yD8oygjXF4Fdoo= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.105.0/go.mod h1:S/+5qZ/2j2QmSpdgC97gLdXhuK71IGeJHoG2D/szuy8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.105.0 h1:HHQOzApli6RjBO0Ujh8OiBIXtFErojbCEBAzsyw/39Q= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.105.0/go.mod h1:96jBGagp5wck48k+qfJpxc3SIj3zLRXbP9E1m97gvLI= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.105.0 h1:Zn3WKPLUXcIQTeu5K2elsi/Ncq14MBRZQPaVdJC+6cQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.105.0/go.mod h1:hzu5KvOYEH5W9OpVc+iRXUAvj7GHcLyyR4qjIH+hOhQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.105.0 h1:hs7TaJClEAhHrj71XcaYqlYnIOpOYQgoHqhnnBNrOK8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.105.0/go.mod h1:ehzaiDdkrww7l1Stvse5GCOAsAZOpFcgeIbB/2PqFs4= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure v0.105.0 h1:PfbW/oTNBNOCzantcwnGXMuc+qhMSUhdcWnfO0qXEhs= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure v0.105.0/go.mod h1:NZsH4m+WpkVKuUYK6Te0Z012jjhfmVcVL3M1W/0Hw4w= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.105.0 h1:EIMptO6ZZeP734nBLxNVftrWA+OEGtgsxarNH7rao2A= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.105.0/go.mod h1:/2F91Hsx7RAn/FENz/7MRbHBkBYQ/uX6t4tYJfyeBfM= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.105.0 h1:60worMGZbZFw6djolg/CVExX6DPQoXgfM4pmdZj2b7E= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.105.0/go.mod h1:Nhq0L1GhTdyl/Td94xCiys0kJMO9lOsezhYRXz0MTvQ= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.105.0 h1:KGvKn2n/tV5aG3JlryEgXnnSVnY0O6YFWGOY72OI8MY= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.105.0/go.mod h1:NKh0a5RFTHnvxRRmjlV96ZzSc5xZrHr1yPRWxskjBB0= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.105.0 h1:JADJYzFGjD3c7eVKYYAHxMFE9rBtMTEPM/4t8C8ww3Q= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.105.0/go.mod h1:rGyiKbLfQyHk5Q+ZSGs4wH8kb2J+SbIlEiytjB/cDdI= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.105.0 h1:JLYDrRk4oJB5CZY49Q1AhvpN8Tnl8faPG0CqpnDOFIw= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.105.0/go.mod h1:73LyO27uXzwtEZrSq/7b1wi5FG9aHx6TUVcCDaxJ5mw= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= @@ -321,8 +321,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -330,8 +330,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/shirou/gopsutil/v4 v4.24.5 h1:gGsArG5K6vmsh5hcFOHaPm87UD003CaDMkAOweSQjhM= -github.com/shirou/gopsutil/v4 v4.24.5/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= +github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64= +github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -397,108 +397,114 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/collector v0.104.0 h1:R3zjM4O3K3+ttzsjPV75P80xalxRbwYTURlK0ys7uyo= -go.opentelemetry.io/collector v0.104.0/go.mod h1:Tm6F3na9ajnOm6I5goU9dURKxq1fSBK1yA94nvUix3k= -go.opentelemetry.io/collector/component v0.104.0 h1:jqu/X9rnv8ha0RNZ1a9+x7OU49KwSMsPbOuIEykHuQE= -go.opentelemetry.io/collector/component v0.104.0/go.mod h1:1C7C0hMVSbXyY1ycCmaMUAR9fVwpgyiNQqxXtEWhVpw= -go.opentelemetry.io/collector/config/configauth v0.104.0 h1:ULtjugImijpKuLgGVt0E0HwiZT7+uDUEtMquh1ODB24= -go.opentelemetry.io/collector/config/configauth v0.104.0/go.mod h1:Til+nLLrQwwhgmfcGTX4ZRcNuMhdaWhBW1jH9DLTabQ= -go.opentelemetry.io/collector/config/configcompression v1.11.0 h1:oTwbcLh7mWHSDUIZXkRJVdNAMoBGS39XF68goTMOQq8= -go.opentelemetry.io/collector/config/configcompression v1.11.0/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= -go.opentelemetry.io/collector/config/configgrpc v0.104.0 h1:E3RtqryQPOm/trJmhlJZj6cCqJNKgv9fOEQvSEpzsFM= -go.opentelemetry.io/collector/config/configgrpc v0.104.0/go.mod h1:tu3ifnJ5pv+4rZcaqNWfvVLjNKb8icSPoClN3THN8PU= -go.opentelemetry.io/collector/config/confighttp v0.104.0 h1:KSY0FSHSjuPyrR6iA2g5oFTozYFpYcy0ssJny8gTNTQ= -go.opentelemetry.io/collector/config/confighttp v0.104.0/go.mod h1:YgSXwuMYHANzzv+IBjHXaBMG/4G2mrseIpICHj+LB3U= -go.opentelemetry.io/collector/config/confignet v0.104.0 h1:i7AOTJf4EQox3SEt1YtQFQR+BwXr3v5D9x3Ai9/ovy8= -go.opentelemetry.io/collector/config/confignet v0.104.0/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= -go.opentelemetry.io/collector/config/configopaque v1.11.0 h1:Pt06PXWVmRaiSX63mzwT8Z9SV/hOc6VHNZbfZ10YY4o= -go.opentelemetry.io/collector/config/configopaque v1.11.0/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= -go.opentelemetry.io/collector/config/configretry v1.11.0 h1:UdEDD0ThxPU7+n2EiKJxVTvDCGygXu9hTfT6LOQv9DY= -go.opentelemetry.io/collector/config/configretry v1.11.0/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= -go.opentelemetry.io/collector/config/configtelemetry v0.104.0 h1:eHv98XIhapZA8MgTiipvi+FDOXoFhCYOwyKReOt+E4E= -go.opentelemetry.io/collector/config/configtelemetry v0.104.0/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= -go.opentelemetry.io/collector/config/configtls v0.104.0 h1:bMmLz2+r+REpO7cDOR+srOJHfitqTZfSZCffDpKfwWk= -go.opentelemetry.io/collector/config/configtls v0.104.0/go.mod h1:e33o7TWcKfe4ToLFyGISEPGMgp6ezf3yHRGY4gs9nKk= -go.opentelemetry.io/collector/config/internal v0.104.0 h1:h3OkxTfXWWrHRyPEGMpJb4fH+54puSBuzm6GQbuEZ2o= -go.opentelemetry.io/collector/config/internal v0.104.0/go.mod h1:KjH43jsAUFyZPeTOz7GrPORMQCK13wRMCyQpWk99gMo= -go.opentelemetry.io/collector/confmap v0.104.0 h1:d3yuwX+CHpoyCh0iMv3rqb/vwAekjSm4ZDL6UK1nZSA= -go.opentelemetry.io/collector/confmap v0.104.0/go.mod h1:F8Lue+tPPn2oldXcfqI75PPMJoyzgUsKVtM/uHZLA4w= -go.opentelemetry.io/collector/confmap/converter/expandconverter v0.104.0 h1:7BhJk71V8xhm8wUpuHG4CVRAPu8JajKj8VmGZ6zS7SA= -go.opentelemetry.io/collector/confmap/converter/expandconverter v0.104.0/go.mod h1:o2xTZJpc65SyYPOAGOjyvWwQEqYSWT4Q4/gMfOYpAzc= -go.opentelemetry.io/collector/confmap/provider/envprovider v0.104.0 h1:/3iSlUHH1Q3xeZc55oVekd4dibXzqgphXZI7EaYJ+ak= -go.opentelemetry.io/collector/confmap/provider/envprovider v0.104.0/go.mod h1:RZDXvP81JwvIGeq3rvDBrRKMUfn2BeKCmppHm4Qm0D8= -go.opentelemetry.io/collector/confmap/provider/fileprovider v0.104.0 h1:B+nMVlIUQxuP52CZSegGuA2z9S+Cv2XwFb2a/TLFPhc= -go.opentelemetry.io/collector/confmap/provider/fileprovider v0.104.0/go.mod h1:O0RcaP/I/kn7JHrwohUfj6AwvQYLxjbqg/HnjkvLLTw= -go.opentelemetry.io/collector/confmap/provider/httpprovider v0.104.0 h1:6UreSAu64Ft3VfKWE3sjcmf+mWMyWemSsrjS/fjRPpQ= -go.opentelemetry.io/collector/confmap/provider/httpprovider v0.104.0/go.mod h1:+vP6R5i9h+oYJNjp4bQHvtSHEu1t+CgSKIeZYZZRQXA= -go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.104.0 h1:y07I19lmp9VHZ58PJ3nwwd1wqumnIBeMxTNBSh/Vn6k= -go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.104.0/go.mod h1:WV1HOa0z3Ln5ZkwEW7Cm2pCHkfzYY9kBe0dLy8DqeYA= -go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.104.0 h1:itBGhyEbX+iz8kz3nc4PYxQx4bL7y87xXNUcGnbKPuY= -go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.104.0/go.mod h1:iPVsTBkRFHZ21UEfSGWk8c4maOzTp6BWWpTk+l6PjJI= -go.opentelemetry.io/collector/connector v0.104.0 h1:Y82ytwZZ+EruWafEebO0dgWMH+TdkcSONEqZ5bm9JYA= -go.opentelemetry.io/collector/connector v0.104.0/go.mod h1:78SEHel3B3taFnSBg/syW4OV9aU1Ec9KjgbgHf/L8JA= -go.opentelemetry.io/collector/connector/forwardconnector v0.104.0 h1:35tTQfTDb+baDcP1F/wdm/mV7CyMKaOx5un4QWEHYRk= -go.opentelemetry.io/collector/connector/forwardconnector v0.104.0/go.mod h1:N5wZtusSfyIUK1qgbfQ+i6y26rFDTvD4QWJHNMnjLQM= -go.opentelemetry.io/collector/consumer v0.104.0 h1:Z1ZjapFp5mUcbkGEL96ljpqLIUMhRgQQpYKkDRtxy+4= -go.opentelemetry.io/collector/consumer v0.104.0/go.mod h1:60zcIb0W9GW0z9uJCv6NmjpSbCfBOeRUyrtEwqK6Hzo= -go.opentelemetry.io/collector/exporter v0.104.0 h1:C2HmnfBa05IQ2T+p9T7K7gXVxjrBLd+JxEtAWo7JNbg= -go.opentelemetry.io/collector/exporter v0.104.0/go.mod h1:Rx0oB0E4Ccg1JuAnEWwhtrq1ygRBkfx4mco1DpR3WaQ= -go.opentelemetry.io/collector/exporter/debugexporter v0.104.0 h1:1Z63H/xxv6IzMP7GPmI6v/lQAqZwYZCVC0rWYcYOomw= -go.opentelemetry.io/collector/exporter/debugexporter v0.104.0/go.mod h1:NHVzTM0Z/bomgR7SAe3ysx4CZzh2UJ3TXWSCnaOB1Wo= -go.opentelemetry.io/collector/exporter/otlpexporter v0.104.0 h1:EFOdhnc2yGhqou0Tud1HsM7fsgWo/H3tdQhYYytDprQ= -go.opentelemetry.io/collector/exporter/otlpexporter v0.104.0/go.mod h1:fAF7Q3Xh0OkxYWUycdrNNDXkyz3nhHIRKDkez0aQ6zg= -go.opentelemetry.io/collector/exporter/otlphttpexporter v0.104.0 h1:JkNCOj7DdyJhcYIaRqtS/X+YtAPRjE4pcruyY6LoM7c= -go.opentelemetry.io/collector/exporter/otlphttpexporter v0.104.0/go.mod h1:6rs4Xugs7tIC3IFbAC+fj56zLiVc7osXC5UTjk/Mkw4= -go.opentelemetry.io/collector/extension v0.104.0 h1:bftkgFMKya/QIwK+bOxEAPVs/TvTez+s1mlaiUznJkA= -go.opentelemetry.io/collector/extension v0.104.0/go.mod h1:x7K0KyM1JGrtLbafEbRoVp0VpGBHpyx9hu87bsja6S4= -go.opentelemetry.io/collector/extension/auth v0.104.0 h1:SelhccGCrqLThPlkbv6lbAowHsjgOTAWcAPz085IEC4= -go.opentelemetry.io/collector/extension/auth v0.104.0/go.mod h1:s3/C7LTSfa91QK0JPMTRIvH/gCv+a4DGiiNeTAX9OhI= -go.opentelemetry.io/collector/extension/ballastextension v0.104.0 h1:5GESICl2J7jBQ6O8dGHuv077ptoIwhwMiF0zEAfcHcU= -go.opentelemetry.io/collector/extension/ballastextension v0.104.0/go.mod h1:TYZBh3PK6fPzTS/2LOklyn/7Lv79k7/WfOsQrlT9cQQ= -go.opentelemetry.io/collector/extension/zpagesextension v0.104.0 h1:rJ9Sw6DR27s6bW7lWBjJhjth5CXpltAHBKIgUFgVwFs= -go.opentelemetry.io/collector/extension/zpagesextension v0.104.0/go.mod h1:85Exj8r237PIvaXL1a/S0KeVNnm3kQNpVXtu0O2Zk5k= -go.opentelemetry.io/collector/featuregate v1.11.0 h1:Z7puIymKoQRm3oNM/NH8reWc2zRPz2PNaJvuokh0lQY= -go.opentelemetry.io/collector/featuregate v1.11.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= -go.opentelemetry.io/collector/otelcol v0.104.0 h1:RnMx7RaSFmX4dq/l3wbXWwcUnFK7RU19AM/0FbMr0Ig= -go.opentelemetry.io/collector/otelcol v0.104.0/go.mod h1:hWFRiHIKT3zbUx6SRevusPRa6mfm+70bPG5CK0glqSU= -go.opentelemetry.io/collector/pdata v1.11.0 h1:rzYyV1zfTQQz1DI9hCiaKyyaczqawN75XO9mdXmR/hE= -go.opentelemetry.io/collector/pdata v1.11.0/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= -go.opentelemetry.io/collector/pdata/pprofile v0.104.0 h1:MYOIHvPlKEJbWLiBKFQWGD0xd2u22xGVLt4jPbdxP4Y= -go.opentelemetry.io/collector/pdata/pprofile v0.104.0/go.mod h1:7WpyHk2wJZRx70CGkBio8klrYTTXASbyIhf+rH4FKnA= -go.opentelemetry.io/collector/pdata/testdata v0.104.0 h1:BKTZ7hIyAX5DMPecrXkVB2e86HwWtJyOlXn/5vSVXNw= -go.opentelemetry.io/collector/pdata/testdata v0.104.0/go.mod h1:3SnYKu8gLfxURJMWS/cFEUFs+jEKS6jvfqKXnOZsdkQ= -go.opentelemetry.io/collector/processor v0.104.0 h1:KSvMDu4DWmK1/k2z2rOzMtTvAa00jnTabtPEK9WOSYI= -go.opentelemetry.io/collector/processor v0.104.0/go.mod h1:qU2/xCCYdvVORkN6aq0H/WUWkvo505VGYg2eOwPvaTg= -go.opentelemetry.io/collector/processor/batchprocessor v0.104.0 h1:6xXvHYkPjwM1zdzliDM2H/omTGgIOkY96JTCln7CFZQ= -go.opentelemetry.io/collector/processor/batchprocessor v0.104.0/go.mod h1:f1VfVdiOlqtJDAvQy8YONEee19nJ3haxNeiMPy59w8M= -go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.104.0 h1:bfxUNxP2i41Dpdp5cXwVuh4ZIQ8g6e4NDnu5HakWQw4= -go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.104.0/go.mod h1:2HtP0f+EBu99Uq07JF20fa2FKAsjnIieOZ4f9Jysfpc= -go.opentelemetry.io/collector/receiver v0.104.0 h1:URL1ExkYYd+qbndm7CdGvI2mxzsv/pNfmwJ+1QSQ9/o= -go.opentelemetry.io/collector/receiver v0.104.0/go.mod h1:+enTCZQLf6dRRANWvykXEzrlRw2JDppXJtoYWd/Dd54= -go.opentelemetry.io/collector/receiver/otlpreceiver v0.104.0 h1:t9cACuSc7kY09guws7VyB/z9QnG7/zWLC1NQ29WH4+o= -go.opentelemetry.io/collector/receiver/otlpreceiver v0.104.0/go.mod h1:sPIIO4F6uit1i/XQgfe2WryvdO5Hr16bQgZTaXcR8mM= -go.opentelemetry.io/collector/semconv v0.104.0 h1:dUvajnh+AYJLEW/XOPk0T0BlwltSdi3vrjO7nSOos3k= -go.opentelemetry.io/collector/semconv v0.104.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= -go.opentelemetry.io/collector/service v0.104.0 h1:DTpkoX4C6qiA3v3cfB2cHv/cH705o5JI9J3P77SFUrE= -go.opentelemetry.io/collector/service v0.104.0/go.mod h1:eq68zgpqRDYaVp60NeRu973J0rA5vZJkezfw/EzxLXc= -go.opentelemetry.io/contrib/config v0.7.0 h1:b1rK5tGTuhhPirJiMxOcyQfZs76j2VapY6ODn3b2Dbs= -go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3TcL8dU5rM5/xcOQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= -go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= -go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= -go.opentelemetry.io/contrib/zpages v0.52.0 h1:MPgkMy0Cp3O5EdfVXP0ss3ujhEibysTM4eszx7E7d+E= -go.opentelemetry.io/contrib/zpages v0.52.0/go.mod h1:fqG5AFdoYru3A3DnhibVuaaEfQV2WKxE7fYE1jgDRwk= +go.opentelemetry.io/collector v0.105.0 h1:Qw/ONVMPT3aD8HjdDRcXCGoZrtSWH3jx4BkwAN1yrEM= +go.opentelemetry.io/collector v0.105.0/go.mod h1:UVapTqB4fJeZpGU/YgOo6665cxCSytqYmMkVmRlu2cg= +go.opentelemetry.io/collector/component v0.105.0 h1:/OdkWHd1xTNX7JRq9iW3AFoJAnYUOGZZyOprNQkGoTI= +go.opentelemetry.io/collector/component v0.105.0/go.mod h1:s8KoxOrhNIBzetkb0LHmzX1OI67DyZbaaUPOWIXS1mg= +go.opentelemetry.io/collector/config/configauth v0.105.0 h1:9Pa65Ay4kdmMsp5mg+/791GvCYy1hHOroIlKBiJzlps= +go.opentelemetry.io/collector/config/configauth v0.105.0/go.mod h1:iL62YzyFCNr1Se0EDYaQ792CFCBiFivSbTWekd4g1VE= +go.opentelemetry.io/collector/config/configcompression v1.12.0 h1:RxqSDVZPJyL7I3v+gdVDvnJ/9tV0ZWgraRDX/gaddfA= +go.opentelemetry.io/collector/config/configcompression v1.12.0/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= +go.opentelemetry.io/collector/config/configgrpc v0.105.0 h1:3IbN6+5c42Bp6CGPKoXZ7QkkQpRxnV01KRse6oD+bd0= +go.opentelemetry.io/collector/config/configgrpc v0.105.0/go.mod h1:wrFPXVXk4so7yYisuPeebZtU6ECzG5aXOYNdfyoHSnI= +go.opentelemetry.io/collector/config/confighttp v0.105.0 h1:0sVdNFuLikgDhxdp+mrbSrYzovwRxZDVIhv9ZQ0X5e4= +go.opentelemetry.io/collector/config/confighttp v0.105.0/go.mod h1:1JMvF8ocjlNxwrVIUJHy9wTEW/nlKs1LLPlTSg4D044= +go.opentelemetry.io/collector/config/confignet v0.105.0 h1:O8kenkWnLPemp2XXVOqFv6OQd9wsnOvBUvl3OlJGPKI= +go.opentelemetry.io/collector/config/confignet v0.105.0/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= +go.opentelemetry.io/collector/config/configopaque v1.12.0 h1:aIsp9NdcLZSiG4YDoFPGXhmma03Tk+6e89+n8GtU/Mc= +go.opentelemetry.io/collector/config/configopaque v1.12.0/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= +go.opentelemetry.io/collector/config/configretry v1.12.0 h1:tEBwueO4AIkwWosxz6NWqnghdZ7y5SfHcIzLrvh6kB8= +go.opentelemetry.io/collector/config/configretry v1.12.0/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= +go.opentelemetry.io/collector/config/configtelemetry v0.105.0 h1:wEfUxAjjstp47aLr2s1cMZiH0dt+k42m6VC6HigqgJA= +go.opentelemetry.io/collector/config/configtelemetry v0.105.0/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= +go.opentelemetry.io/collector/config/configtls v1.12.0 h1:Px0+GE4LE/9sXMgkwBb5g8QHWvnrnuRg9BLSa+QtxgM= +go.opentelemetry.io/collector/config/configtls v1.12.0/go.mod h1:aeCGPlvrWhc+EySpIKdelPAj4l9wXKzZPouQO3NIoTs= +go.opentelemetry.io/collector/config/internal v0.105.0 h1:PWnbeslkIGMjZzh5IJRjO6bA02d1Xrkjw2N60ixWzqQ= +go.opentelemetry.io/collector/config/internal v0.105.0/go.mod h1:+Y5vRJ+lio2uuYlVPfy9AZVrip9Y0B9PiUA5Vz7lzZw= +go.opentelemetry.io/collector/confmap v0.105.0 h1:3NP2BbUju42rjeQvRbmpCJGJGvbiV3WnGyXsVmocimo= +go.opentelemetry.io/collector/confmap v0.105.0/go.mod h1:Oj1xUBRvAuL8OWWMj9sSYf1uQpB+AErpj+FKGUQLBI0= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.105.0 h1:1c81AJg+WP+dGoAEftmc0KF5RK0mh4bxIFlU902CnFI= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.105.0/go.mod h1:ImkfsdJToeP3ZZ5ZVmQUdaKEueVHiZbwrQGyoDx/EiM= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.105.0 h1:r+5ZxhDLYqd6klLky3gtgWHgG5L2LX6o6VIvaLG39Gs= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.105.0/go.mod h1:Srzkp9izW29pJtwoVtpL+bAN5ibtv+fyLg9JWZzCCi8= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.105.0 h1:RgQwEbJgx+BihDt8yhiqmEi8IjQYFwbzsYRcTzvM7E4= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.105.0/go.mod h1:P7oT+6eaMv+ZyPSu6SEKalfR/UALdQgE3hq36b0Iww4= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.105.0 h1:hgzauRlEZId1AM00g3EHl4GaR4GLG23sdvmSRWQ9iFQ= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.105.0/go.mod h1:TqZbmLcG9GvLp76q45eE0EwiTHBmj4qRP32fI48DH5Q= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.105.0 h1:xQr2I7iQPDkFP/E2H9lg6jfAtpY1iz2oLGEFwa2FaeE= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.105.0/go.mod h1:UVTcdI8WhTXl8duc0EkeJU7nSQNptHwE6D/eqgeand0= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.105.0 h1:SAF6JFrjQ8YkFnggA5iiw9eKiBYDaO6ynK0Dl+vzIgw= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.105.0/go.mod h1:bWMg+SNk/PwEMiLWV0eCFNL4Er59aXsdDKq+2JzMOkE= +go.opentelemetry.io/collector/connector v0.105.0 h1:hmTsI6rnPAXsdVJ+wYrgeNTCrQzxzglU37qPlNSKTy0= +go.opentelemetry.io/collector/connector v0.105.0/go.mod h1:xQnq1Ygd/F4Kb93E0UsKus5YDINeyQ6GzCkxoujI9TE= +go.opentelemetry.io/collector/connector/forwardconnector v0.105.0 h1:e9gvf11xfijwraZLBf3IHXEk1YpXxMoJfqXarYo7ZNU= +go.opentelemetry.io/collector/connector/forwardconnector v0.105.0/go.mod h1:XIeSscBCAcpywFa7oVwTOpdHjRojS7No6Yz34YhFBp4= +go.opentelemetry.io/collector/consumer v0.105.0 h1:pO5Tspoz7yvEs81+904HfDjByP8Z7uuNk+7pOr3lRHM= +go.opentelemetry.io/collector/consumer v0.105.0/go.mod h1:tnaPDHUfKBJ01OnsJNRecniG9iciE+xHYLqamYwFQOQ= +go.opentelemetry.io/collector/exporter v0.105.0 h1:O2xmjfaRbkbpo3XkwEcnuBHCoXc5kS9CjYO8geu+3vo= +go.opentelemetry.io/collector/exporter v0.105.0/go.mod h1:5ulGEHRZyGbX4DWHJa2Br6Fr/W1Lay8ayf++1WrVvgk= +go.opentelemetry.io/collector/exporter/debugexporter v0.105.0 h1:Osqo7+pr1vZbt+12KMzZrst6uMv1r8UJZjTgI6vwqL4= +go.opentelemetry.io/collector/exporter/debugexporter v0.105.0/go.mod h1:rqjWoduh/qj6OlttzdbB8Ji6Vp4SoHJRnZHNuS/Mkp0= +go.opentelemetry.io/collector/exporter/otlpexporter v0.105.0 h1:JWAlh7A1oNm4LqUrrtPjAmUdvcE11VZCeJhMk0lShYg= +go.opentelemetry.io/collector/exporter/otlpexporter v0.105.0/go.mod h1:aA2E+t2giwUoJ2RmDw+2dVox64J2zHpTT2m/mi7zOIM= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0 h1:ciJvNlC9ztoeyIi23TCpZ6PN7l75tl/yvKqBo0Cmajw= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0/go.mod h1:e+PdCkQkP+1cj0+cijRfmE4/VjofxeeWoS1cvvu39HI= +go.opentelemetry.io/collector/extension v0.105.0 h1:R8i4HMvuSm20Nt3onyrLk19KKhjCNAsgS8FGh60rcZU= +go.opentelemetry.io/collector/extension v0.105.0/go.mod h1:oyX960URG27esNKitf3o2rqcBj0ajcx+dxkCxwRz34U= +go.opentelemetry.io/collector/extension/auth v0.105.0 h1:5gzRSHU0obVtZDzLLJQ/p4sIkacUsyEEpBiBRDs82Hk= +go.opentelemetry.io/collector/extension/auth v0.105.0/go.mod h1:zf45v7u1nKbdDHeMuhBVdSFwhbq2w9IWCbFKcDSkW5I= +go.opentelemetry.io/collector/extension/ballastextension v0.105.0 h1:JAihRR9cu35lGI6mGvxrwVcUHaPO7n++RaC4oouZics= +go.opentelemetry.io/collector/extension/ballastextension v0.105.0/go.mod h1:2PgAJVYhBDWv3gl/vZ6nblHoVQMrwWheKE+RjXFozdM= +go.opentelemetry.io/collector/extension/zpagesextension v0.105.0 h1:rD77+hMPaSuBOh8w6un+x0VI5oM2oAsSzAC9nKTNauE= +go.opentelemetry.io/collector/extension/zpagesextension v0.105.0/go.mod h1:vDuL/PBfZfHpOyF2XCJ/jWfHn/hu2pCE5KVRGCHOX+k= +go.opentelemetry.io/collector/featuregate v1.12.0 h1:l5WbV2vMQd2bL8ubfGrbKNtZaeJRckE12CTHvRe47Tw= +go.opentelemetry.io/collector/featuregate v1.12.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= +go.opentelemetry.io/collector/internal/globalgates v0.105.0 h1:U/CwnTUXtrblD1sZ6ri7KWfYoTNjQd7GjJKrX/phRik= +go.opentelemetry.io/collector/internal/globalgates v0.105.0/go.mod h1:Z5US6O2xkZAtxVSSBnHAPFZwPhFoxlyKLUvS67Vx4gc= +go.opentelemetry.io/collector/otelcol v0.105.0 h1:2YMp/OckEc4qEfIjx3pi7W7xr+nW03KdrdhHAt3NmYY= +go.opentelemetry.io/collector/otelcol v0.105.0/go.mod h1:SwdiOY/0szbyuJ1sDDv66W5nFPVkA9hOhQoEBAFbHA0= +go.opentelemetry.io/collector/pdata v1.12.0 h1:Xx5VK1p4VO0md8MWm2icwC1MnJ7f8EimKItMWw46BmA= +go.opentelemetry.io/collector/pdata v1.12.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI= +go.opentelemetry.io/collector/pdata/pprofile v0.105.0 h1:C+Hd7CNcepL/364OBV9f4lHzJil2jQSOxcEM1PFXGDg= +go.opentelemetry.io/collector/pdata/pprofile v0.105.0/go.mod h1:chr7lMJIzyXkccnPRkIPhyXtqLZLSReZYhwsggOGEfg= +go.opentelemetry.io/collector/pdata/testdata v0.105.0 h1:5sPZzanR4nJR3sNQk3MTdArdEZCK0NRAfC29t0Dtf60= +go.opentelemetry.io/collector/pdata/testdata v0.105.0/go.mod h1:NIfgaclQp/M1BZhgyc/7hDWD+/DumC/OMBQVI2KW+N0= +go.opentelemetry.io/collector/processor v0.105.0 h1:LE6wEMWNa3h7eOJLMBm2lA0v6sc2j6Geqv1e3pIWS8Y= +go.opentelemetry.io/collector/processor v0.105.0/go.mod h1:QAvMEtd3k+YhRrnaEgs/8e0UueYonuT8Hg4udQOht60= +go.opentelemetry.io/collector/processor/batchprocessor v0.105.0 h1:j9f7EWmaR6kaQubKE0L3lvZxoIEppRyTAvqLDmp2Jig= +go.opentelemetry.io/collector/processor/batchprocessor v0.105.0/go.mod h1:klVt/0Egd7LSgWyxlSpgNvLMQRaqfUJg7Nv4qdpQOHA= +go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.105.0 h1:qPjFg7uWmsVvf0Kk11WzM6Zy//zNZFC5uFwYKFXlEn8= +go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.105.0/go.mod h1:0pgvBY04E2bRq5dY3Qfj66mKpkFnu252FNm7iiFEiAU= +go.opentelemetry.io/collector/receiver v0.105.0 h1:eZF97kMUnKJ20Uc4PaDlgLIGmaA8kyLqhH+vMXjh92U= +go.opentelemetry.io/collector/receiver v0.105.0/go.mod h1:nGKDXLUGVHxMBJ5QLfsJ/bIhGvoMGqsN0pZtD5SC8sE= +go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0 h1:1qjnvrnDcEaj93WgyrWRWTTAGNODaxi98wNcXiHDwfM= +go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0/go.mod h1:XGs9OAQQFDfVhSzruLcFNdzhTw2kbZZAV68iRE9ecGA= +go.opentelemetry.io/collector/semconv v0.105.0 h1:8p6dZ3JfxFTjbY38d8xlQGB1TQ3nPUvs+D0RERniZ1g= +go.opentelemetry.io/collector/semconv v0.105.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= +go.opentelemetry.io/collector/service v0.105.0 h1:93dqJVNKToEvYDYZO2pHrN8f6Uev4+xhzzmw4qwgAzE= +go.opentelemetry.io/collector/service v0.105.0/go.mod h1:H0fD5SZjV2AZTgHEjRhObnP8YrhG2etAh9rsWLtmimQ= +go.opentelemetry.io/contrib/config v0.8.0 h1:OD7aDMhL+2EpzdSHfkDmcdD/uUA+PgKM5faFyF9XFT0= +go.opentelemetry.io/contrib/config v0.8.0/go.mod h1:dGeVZWE//3wrxYHHP0iCBYJU1QmOmPcbV+FNB7pjDYI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0 h1:XR6CFQrQ/ttAYmTBX2loUEFGdk1h17pxYI8828dk/1Y= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0/go.mod h1:DWRkzJONLquRz7OJPh2rRbZ7MugQj62rk7g6HRnEqh0= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.22.0 h1:OYxqumWcd1yaV/qvCt1B7Sru9OeUNGjeXq/oldx3AGk= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.22.0/go.mod h1:2tZTRqCbvx7nG57wUwd5NQpNVujOWnR84iPLllIH0Ok= +go.opentelemetry.io/contrib/zpages v0.53.0 h1:hGgaJ3nrescxEk383gOBHA5gNfoquHs8oV/XcKYxJkw= +go.opentelemetry.io/contrib/zpages v0.53.0/go.mod h1:iOo8fpUxMAu5+4x9DSEQeUOCeY19KaN6v2OPSeIggz4= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= -go.opentelemetry.io/otel/bridge/opencensus v1.27.0/go.mod h1:uRvWtAAXzyVOST0WMPX5JHGBaAvBws+2F8PcC5gMnTk= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/bridge/opencensus v1.28.0 h1:/BcyAV1bUJjSVxoeKwTQL9cS4X1iC6izZ9mheeuVSCU= +go.opentelemetry.io/otel/bridge/opencensus v1.28.0/go.mod h1:FZp2xE+46yAyp3DfLFALze58nY0iIE8zs+mCgkPAzq0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 h1:zBPZAISA9NOc5cE8zydqDiS0itvg/P/0Hn9m72a5gvM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0/go.mod h1:gcj2fFjEsqpV3fXuzAA+0Ze1p2/4MJ4T7d77AmkvueQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= @@ -507,14 +513,18 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 h1:/jlt1Y8gXWiHG9FBx6cJaIC5hYx5Fe64nC8w5Cylt/0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0/go.mod h1:bmToOGOBZ4hA9ghphIc1PAf66VA8KOtsuy3+ScStG20= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9lc7aFmBwkWxY/RI1RDdXepSF6y8TPE= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= +go.opentelemetry.io/otel/log v0.4.0 h1:/vZ+3Utqh18e8TPjuc3ecg284078KWrR8BRz+PQAj3o= +go.opentelemetry.io/otel/log v0.4.0/go.mod h1:DhGnQvky7pHy82MIRV43iXh3FlKN8UUKftn0KbLOq6I= go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/log v0.4.0 h1:1mMI22L82zLqf6KtkjrRy5BbagOTWdJsqMY/HSqILAA= +go.opentelemetry.io/otel/sdk/log v0.4.0/go.mod h1:AYJ9FVF0hNOgAVzUG/ybg/QttnXhUePWAupmCqtdESo= go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= @@ -539,8 +549,8 @@ golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -579,8 +589,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -616,8 +626,8 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/internal/grpctest/reflection.go b/internal/grpctest/reflection.go index 8aba438531d..c1e6e6a0c47 100644 --- a/internal/grpctest/reflection.go +++ b/internal/grpctest/reflection.go @@ -27,7 +27,6 @@ import ( // ReflectionServiceValidator verifies that a gRPC service at a given address // supports reflection service. Called must invoke Execute func. type ReflectionServiceValidator struct { - Server *grpc.Server HostPort string ExpectedServices []string } diff --git a/internal/grpctest/reflection_test.go b/internal/grpctest/reflection_test.go index a814e5418e7..af6ebe7290b 100644 --- a/internal/grpctest/reflection_test.go +++ b/internal/grpctest/reflection_test.go @@ -41,7 +41,6 @@ func TestReflectionServiceValidator(t *testing.T) { ReflectionServiceValidator{ HostPort: listener.Addr().String(), - Server: server, ExpectedServices: []string{"grpc.reflection.v1alpha.ServerReflection"}, }.Execute(t) } diff --git a/internal/tracegen/worker_test.go b/internal/tracegen/worker_test.go index 764ebfd54a5..557e8060cbe 100644 --- a/internal/tracegen/worker_test.go +++ b/internal/tracegen/worker_test.go @@ -16,30 +16,47 @@ import ( ) func Test_SimulateTraces(t *testing.T) { - logger, buf := testutils.NewLogger() - tp := sdktrace.NewTracerProvider() - tracers := []trace.Tracer{tp.Tracer("stdout")} - wg := sync.WaitGroup{} - wg.Add(1) - var running uint32 = 1 - - worker := &worker{ - logger: logger, - tracers: tracers, - wg: &wg, - id: 7, - running: &running, - Config: Config{ - Traces: 7, - Duration: time.Second, - Pause: time.Second, - Service: "stdout", - Debug: true, - Firehose: true, + tests := []struct { + name string + pause time.Duration + }{ + { + name: "no pause", + pause: 0, + }, + { + name: "with pause", + pause: time.Second, }, } - expectedOutput := `{"level":"info","msg":"Worker 7 generated 7 traces"}` + "\n" - worker.simulateTraces() - assert.Equal(t, expectedOutput, buf.String()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger, buf := testutils.NewLogger() + tp := sdktrace.NewTracerProvider() + tracers := []trace.Tracer{tp.Tracer("stdout")} + wg := sync.WaitGroup{} + wg.Add(1) + var running uint32 = 1 + worker := &worker{ + logger: logger, + tracers: tracers, + wg: &wg, + id: 7, + running: &running, + Config: Config{ + Traces: 7, + Duration: time.Second, + Pause: tt.pause, + Service: "stdout", + Debug: true, + Firehose: true, + ChildSpans: 1, + }, + } + expectedOutput := `{"level":"info","msg":"Worker 7 generated 7 traces"}` + "\n" + worker.simulateTraces() + assert.Equal(t, expectedOutput, buf.String()) + }) + } } diff --git a/jaeger-ui b/jaeger-ui index 560c749b481..1704a9a66ae 160000 --- a/jaeger-ui +++ b/jaeger-ui @@ -1 +1 @@ -Subproject commit 560c749b481f8244bf76b0afe001174fa9ce11f5 +Subproject commit 1704a9a66ae780ec6bb2f524116176aa9c8a23f8 diff --git a/pkg/clientcfg/clientcfghttp/handler.go b/pkg/clientcfg/clientcfghttp/handler.go index ab2cef08c5d..7bc22be9a4e 100644 --- a/pkg/clientcfg/clientcfghttp/handler.go +++ b/pkg/clientcfg/clientcfghttp/handler.go @@ -109,6 +109,17 @@ func (h *HTTPHandler) RegisterRoutes(router *mux.Router) { }).Methods(http.MethodGet) } +// RegisterRoutes registers configuration handlers with HTTP Router. +func (h *HTTPHandler) RegisterRoutesWithHTTP(router *http.ServeMux) { + prefix := h.params.BasePath + router.HandleFunc( + prefix+"/", + func(w http.ResponseWriter, r *http.Request) { + h.serveSamplingHTTP(w, r, h.encodeThriftLegacy) + }, + ) +} + func (h *HTTPHandler) serviceFromRequest(w http.ResponseWriter, r *http.Request) (string, error) { services := r.URL.Query()["service"] if len(services) != 1 { diff --git a/pkg/clientcfg/clientcfghttp/handler_test.go b/pkg/clientcfg/clientcfghttp/handler_test.go index 6afc30d61b2..723047c1ed3 100644 --- a/pkg/clientcfg/clientcfghttp/handler_test.go +++ b/pkg/clientcfg/clientcfghttp/handler_test.go @@ -47,6 +47,7 @@ func withServer( basePath string, mockSamplingResponse *api_v2.SamplingStrategyResponse, mockBaggageResponse []*baggage.BaggageRestriction, + withGorilla bool, testFn func(server *testServer), ) { metricsFactory := metricstest.NewFactory(0) @@ -62,9 +63,18 @@ func withServer( BasePath: basePath, LegacySamplingEndpoint: true, }) - r := mux.NewRouter() - handler.RegisterRoutes(r) - server := httptest.NewServer(r) + + var server *httptest.Server + if withGorilla { + r := mux.NewRouter() + handler.RegisterRoutes(r) + server = httptest.NewServer(r) + } else { + mux := http.NewServeMux() + handler.RegisterRoutesWithHTTP(mux) + server = httptest.NewServer(mux) + } + defer server.Close() testFn(&testServer{ metricsFactory: metricsFactory, @@ -76,15 +86,17 @@ func withServer( } func TestHTTPHandler(t *testing.T) { + testGorillaHTTPHandler(t, "") testHTTPHandler(t, "") } func TestHTTPHandlerWithBasePath(t *testing.T) { + testGorillaHTTPHandler(t, "/foo") testHTTPHandler(t, "/foo") } -func testHTTPHandler(t *testing.T, basePath string) { - withServer(basePath, rateLimiting(42), restrictions("luggage", 10), func(ts *testServer) { +func testGorillaHTTPHandler(t *testing.T, basePath string) { + withServer(basePath, rateLimiting(42), restrictions("luggage", 10), true, func(ts *testServer) { tests := []struct { endpoint string expOutput string @@ -146,6 +158,49 @@ func testHTTPHandler(t *testing.T, basePath string) { }) } +func testHTTPHandler(t *testing.T, basePath string) { + withServer(basePath, rateLimiting(42), restrictions("luggage", 10), false, func(ts *testServer) { + tests := []struct { + endpoint string + expOutput string + }{ + { + endpoint: "/", + expOutput: `{"strategyType":1,"rateLimitingSampling":{"maxTracesPerSecond":42}}`, + }, + } + for _, test := range tests { + t.Run("endpoint="+test.endpoint, func(t *testing.T) { + resp, err := http.Get(ts.server.URL + basePath + test.endpoint + "?service=Y") + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + err = resp.Body.Close() + require.NoError(t, err) + assert.Equal(t, test.expOutput, string(body)) + if test.endpoint == "/" { + objResp := &tSampling092.SamplingStrategyResponse{} + require.NoError(t, json.Unmarshal(body, objResp)) + assert.EqualValues(t, + ts.samplingProvider.samplingResponse.GetStrategyType(), + objResp.GetStrategyType()) + assert.EqualValues(t, + ts.samplingProvider.samplingResponse.GetRateLimitingSampling().GetMaxTracesPerSecond(), + objResp.GetRateLimitingSampling().GetMaxTracesPerSecond()) + } else { + objResp, err := p2json.SamplingStrategyResponseFromJSON(body) + require.NoError(t, err) + assert.EqualValues(t, ts.samplingProvider.samplingResponse, objResp) + } + }) + } + + // handler must emit metrics + ts.metricsFactory.AssertCounterMetrics(t, metricstest.ExpectedMetric{Name: "http-server.requests", Tags: map[string]string{"type": "sampling-legacy"}, Value: 1}) + }) +} + func TestHTTPHandlerErrors(t *testing.T) { testCases := []struct { description string @@ -215,61 +270,67 @@ func TestHTTPHandlerErrors(t *testing.T) { for _, tc := range testCases { testCase := tc // capture loop var t.Run(testCase.description, func(t *testing.T) { - withServer("", testCase.mockSamplingResponse, testCase.mockBaggageResponse, func(ts *testServer) { - resp, err := http.Get(ts.server.URL + testCase.url) - require.NoError(t, err) - assert.Equal(t, testCase.statusCode, resp.StatusCode) - if testCase.body != "" { - body, err := io.ReadAll(resp.Body) + for _, withGorilla := range []bool{true, false} { + withServer("", testCase.mockSamplingResponse, testCase.mockBaggageResponse, withGorilla, func(ts *testServer) { + resp, err := http.Get(ts.server.URL + testCase.url) require.NoError(t, err) - assert.Equal(t, testCase.body, string(body)) - } + assert.Equal(t, testCase.statusCode, resp.StatusCode) + if testCase.body != "" { + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.Equal(t, testCase.body, string(body)) + } - if len(testCase.metrics) > 0 { - ts.metricsFactory.AssertCounterMetrics(t, testCase.metrics...) - } - }) + if len(testCase.metrics) > 0 { + ts.metricsFactory.AssertCounterMetrics(t, testCase.metrics...) + } + }) + } }) } t.Run("failure to write a response", func(t *testing.T) { - withServer("", probabilistic(0.001), restrictions("luggage", 10), func(ts *testServer) { - handler := ts.handler + for _, withGorilla := range []bool{true, false} { + withServer("", probabilistic(0.001), restrictions("luggage", 10), withGorilla, func(ts *testServer) { + handler := ts.handler - req := httptest.NewRequest("GET", "http://localhost:80/?service=X", nil) - w := &mockWriter{header: make(http.Header)} - handler.serveSamplingHTTP(w, req, handler.encodeThriftLegacy) + req := httptest.NewRequest("GET", "http://localhost:80/?service=X", nil) + w := &mockWriter{header: make(http.Header)} + handler.serveSamplingHTTP(w, req, handler.encodeThriftLegacy) - ts.metricsFactory.AssertCounterMetrics(t, - metricstest.ExpectedMetric{Name: "http-server.errors", Tags: map[string]string{"source": "write", "status": "5xx"}, Value: 1}) + ts.metricsFactory.AssertCounterMetrics(t, + metricstest.ExpectedMetric{Name: "http-server.errors", Tags: map[string]string{"source": "write", "status": "5xx"}, Value: 1}) - req = httptest.NewRequest("GET", "http://localhost:80/baggageRestrictions?service=X", nil) - handler.serveBaggageHTTP(w, req) + req = httptest.NewRequest("GET", "http://localhost:80/baggageRestrictions?service=X", nil) + handler.serveBaggageHTTP(w, req) - ts.metricsFactory.AssertCounterMetrics(t, - metricstest.ExpectedMetric{Name: "http-server.errors", Tags: map[string]string{"source": "write", "status": "5xx"}, Value: 2}) - }) + ts.metricsFactory.AssertCounterMetrics(t, + metricstest.ExpectedMetric{Name: "http-server.errors", Tags: map[string]string{"source": "write", "status": "5xx"}, Value: 2}) + }) + } }) } func TestEncodeErrors(t *testing.T) { - withServer("", nil, nil, func(server *testServer) { - _, err := server.handler.encodeThriftLegacy(&api_v2.SamplingStrategyResponse{ - StrategyType: -1, - }) - require.Error(t, err) - assert.Contains(t, err.Error(), "ConvertSamplingResponseFromDomain failed") - server.metricsFactory.AssertCounterMetrics(t, []metricstest.ExpectedMetric{ - {Name: "http-server.errors", Tags: map[string]string{"source": "thrift", "status": "5xx"}, Value: 1}, - }...) + for _, withGorilla := range []bool{true, false} { + withServer("", nil, nil, withGorilla, func(server *testServer) { + _, err := server.handler.encodeThriftLegacy(&api_v2.SamplingStrategyResponse{ + StrategyType: -1, + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "ConvertSamplingResponseFromDomain failed") + server.metricsFactory.AssertCounterMetrics(t, []metricstest.ExpectedMetric{ + {Name: "http-server.errors", Tags: map[string]string{"source": "thrift", "status": "5xx"}, Value: 1}, + }...) - _, err = server.handler.encodeProto(nil) - require.Error(t, err) - assert.Contains(t, err.Error(), "SamplingStrategyResponseToJSON failed") - server.metricsFactory.AssertCounterMetrics(t, []metricstest.ExpectedMetric{ - {Name: "http-server.errors", Tags: map[string]string{"source": "proto", "status": "5xx"}, Value: 1}, - }...) - }) + _, err = server.handler.encodeProto(nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "SamplingStrategyResponseToJSON failed") + server.metricsFactory.AssertCounterMetrics(t, []metricstest.ExpectedMetric{ + {Name: "http-server.errors", Tags: map[string]string{"source": "proto", "status": "5xx"}, Value: 1}, + }...) + }) + } } func rateLimiting(rate int32) *api_v2.SamplingStrategyResponse { diff --git a/pkg/kafka/auth/config_test.go b/pkg/kafka/auth/config_test.go new file mode 100644 index 00000000000..a32b0077c02 --- /dev/null +++ b/pkg/kafka/auth/config_test.go @@ -0,0 +1,174 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package auth + +import ( + "flag" + "testing" + + "github.com/Shopify/sarama" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest" + + "github.com/jaegertracing/jaeger/pkg/config" + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" +) + +func addFlags(flags *flag.FlagSet) { + configPrefix := "kafka.auth" + AddFlags(configPrefix, flags) +} + +func Test_InitFromViper(t *testing.T) { + configPrefix := "kafka.auth" + v, command := config.Viperize(addFlags) + command.ParseFlags([]string{ + "--kafka.auth.authentication=tls", + "--kafka.auth.kerberos.service-name=kafka", + "--kafka.auth.kerberos.realm=EXAMPLE.COM", + "--kafka.auth.kerberos.use-keytab=true", + "--kafka.auth.kerberos.username=user", + "--kafka.auth.kerberos.password=password", + "--kafka.auth.kerberos.config-file=/path/to/krb5.conf", + "--kafka.auth.kerberos.keytab-file=/path/to/keytab", + "--kafka.auth.kerberos.disable-fast-negotiation=true", + "--kafka.auth.tls.enabled=false", + "--kafka.auth.plaintext.username=user", + "--kafka.auth.plaintext.password=password", + "--kafka.auth.plaintext.mechanism=SCRAM-SHA-256", + "--kafka.auth.tls.ca=failing", + }) + + authConfig := &AuthenticationConfig{} + err := authConfig.InitFromViper(configPrefix, v) + require.EqualError(t, err, "failed to process Kafka TLS options: kafka.auth.tls.* options cannot be used when kafka.auth.tls.enabled is false") + + command.ParseFlags([]string{"--kafka.auth.tls.ca="}) + v.BindPFlags(command.Flags()) + err = authConfig.InitFromViper(configPrefix, v) + require.NoError(t, err) + + expectedConfig := &AuthenticationConfig{ + Authentication: "tls", + Kerberos: KerberosConfig{ + ServiceName: "kafka", + Realm: "EXAMPLE.COM", + UseKeyTab: true, + Username: "user", + Password: "password", + ConfigPath: "/path/to/krb5.conf", + KeyTabPath: "/path/to/keytab", + DisablePAFXFast: true, + }, + TLS: tlscfg.Options{ + Enabled: true, + }, + PlainText: PlainTextConfig{ + Username: "user", + Password: "password", + Mechanism: "SCRAM-SHA-256", + }, + } + assert.Equal(t, expectedConfig, authConfig) +} + +// Test plaintext with different mechanisms +func testPlaintext(v *viper.Viper, t *testing.T, configPrefix string, logger *zap.Logger, mechanism string, saramaConfig *sarama.Config) { + v.Set(configPrefix+plainTextPrefix+suffixPlainTextMechanism, mechanism) + authConfig := &AuthenticationConfig{} + err := authConfig.InitFromViper(configPrefix, v) + require.NoError(t, err) + require.NoError(t, authConfig.SetConfiguration(saramaConfig, logger)) +} + +func TestSetConfiguration(t *testing.T) { + logger := zaptest.NewLogger(t) + saramaConfig := sarama.NewConfig() + configPrefix := "kafka.auth" + v, command := config.Viperize(addFlags) + + // Table-driven test cases + tests := []struct { + name string + authType string + expectedError string + plainTextMechanisms []string + }{ + { + name: "Invalid authentication method", + authType: "fail", + expectedError: "Unknown/Unsupported authentication method fail to kafka cluster", + }, + { + name: "Kerberos authentication", + authType: "kerberos", + expectedError: "", + }, + { + name: "Plaintext authentication with SCRAM-SHA-256", + authType: "plaintext", + expectedError: "", + plainTextMechanisms: []string{"SCRAM-SHA-256"}, + }, + { + name: "Plaintext authentication with SCRAM-SHA-512", + authType: "plaintext", + expectedError: "", + plainTextMechanisms: []string{"SCRAM-SHA-512"}, + }, + { + name: "Plaintext authentication with PLAIN", + authType: "plaintext", + expectedError: "", + plainTextMechanisms: []string{"PLAIN"}, + }, + { + name: "No authentication", + authType: " ", + expectedError: "", + }, + { + name: "TLS authentication", + authType: "tls", + expectedError: "", + }, + { + name: "TLS authentication with invalid cipher suite", + authType: "tls", + expectedError: "error loading tls config: failed to get cipher suite ids from cipher suite names: cipher suite fail not supported or doesn't exist", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + command.ParseFlags([]string{ + "--kafka.auth.authentication=" + tt.authType, + }) + authConfig := &AuthenticationConfig{} + defer authConfig.TLS.Close() + err := authConfig.InitFromViper(configPrefix, v) + require.NoError(t, err) + + if tt.authType == "tls" && tt.expectedError != "" { + authConfig.TLS.CipherSuites = []string{"fail"} + } + + if len(tt.plainTextMechanisms) > 0 { + for _, mechanism := range tt.plainTextMechanisms { + testPlaintext(v, t, configPrefix, logger, mechanism, saramaConfig) + } + } else { + err = authConfig.SetConfiguration(saramaConfig, logger) + if tt.expectedError != "" { + require.EqualError(t, err, tt.expectedError) + } else { + require.NoError(t, err) + } + } + }) + } +} diff --git a/pkg/kafka/auth/kerberos_test.go b/pkg/kafka/auth/kerberos_test.go new file mode 100644 index 00000000000..a86a250c8f0 --- /dev/null +++ b/pkg/kafka/auth/kerberos_test.go @@ -0,0 +1,68 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package auth + +import ( + "testing" + + "github.com/Shopify/sarama" + "github.com/stretchr/testify/assert" +) + +func TestSetKerberosConfiguration(t *testing.T) { + tests := []struct { + name string + config KerberosConfig + }{ + { + name: "With KeyTab", + config: KerberosConfig{ + ServiceName: "service", + Realm: "realm", + UseKeyTab: true, + Username: "username", + Password: "password", + ConfigPath: "/path/to/config", + KeyTabPath: "/path/to/keytab", + DisablePAFXFast: true, + }, + }, + { + name: "Without KeyTab", + config: KerberosConfig{ + ServiceName: "service", + Realm: "realm", + UseKeyTab: false, + Username: "username", + Password: "password", + ConfigPath: "/path/to/config", + DisablePAFXFast: false, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + saramaConfig := sarama.NewConfig() + + setKerberosConfiguration(&tt.config, saramaConfig) + + assert.Equal(t, sarama.SASLMechanism("GSSAPI"), saramaConfig.Net.SASL.Mechanism) + assert.True(t, saramaConfig.Net.SASL.Enable) + assert.Equal(t, tt.config.Username, saramaConfig.Net.SASL.GSSAPI.Username) + assert.Equal(t, tt.config.Realm, saramaConfig.Net.SASL.GSSAPI.Realm) + assert.Equal(t, tt.config.ServiceName, saramaConfig.Net.SASL.GSSAPI.ServiceName) + assert.Equal(t, tt.config.DisablePAFXFast, saramaConfig.Net.SASL.GSSAPI.DisablePAFXFAST) + assert.Equal(t, tt.config.ConfigPath, saramaConfig.Net.SASL.GSSAPI.KerberosConfigPath) + + if tt.config.UseKeyTab { + assert.Equal(t, tt.config.KeyTabPath, saramaConfig.Net.SASL.GSSAPI.KeyTabPath) + assert.Equal(t, sarama.KRB5_KEYTAB_AUTH, saramaConfig.Net.SASL.GSSAPI.AuthType) + } else { + assert.Equal(t, tt.config.Password, saramaConfig.Net.SASL.GSSAPI.Password) + assert.Equal(t, sarama.KRB5_USER_AUTH, saramaConfig.Net.SASL.GSSAPI.AuthType) + } + }) + } +} diff --git a/pkg/kafka/auth/plaintext_test.go b/pkg/kafka/auth/plaintext_test.go new file mode 100644 index 00000000000..623d579d5e9 --- /dev/null +++ b/pkg/kafka/auth/plaintext_test.go @@ -0,0 +1,100 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package auth + +import ( + "fmt" + "strings" + "testing" + + "github.com/Shopify/sarama" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/xdg-go/scram" +) + +func TestScramClient(t *testing.T) { + scramClientFunc := clientGenFunc(scram.SHA256) + client := scramClientFunc().(*scramClient) + + err := client.Begin("testUser", "testPassword", "testAuthzID") + require.NoError(t, err, "Begin should not return an error") + assert.NotNil(t, client.Client, "Client should be initialized") + assert.NotNil(t, client.ClientConversation, "ClientConversation should be initialized") + + step, err := client.Step("testChallenge") + require.NoError(t, err, "Step should not return an error") + require.NotEmpty(t, step, "Step should return a non-empty response") + + done := client.Done() + assert.False(t, done, "Done should return false initially") +} + +func TestSetPlainTextConfiguration(t *testing.T) { + tests := []struct { + config PlainTextConfig + expectedError error + expectedMechanism sarama.SASLMechanism + }{ + { + config: PlainTextConfig{ + Username: "username", + Password: "password", + Mechanism: "SCRAM-SHA-256", + }, + expectedError: nil, + expectedMechanism: sarama.SASLTypeSCRAMSHA256, + }, + { + config: PlainTextConfig{ + Username: "username", + Password: "password", + Mechanism: "SCRAM-SHA-512", + }, + expectedError: nil, + expectedMechanism: sarama.SASLTypeSCRAMSHA512, + }, + { + config: PlainTextConfig{ + Username: "username", + Password: "password", + Mechanism: "PLAIN", + }, + expectedError: nil, + expectedMechanism: sarama.SASLTypePlaintext, + }, + { + config: PlainTextConfig{ + Username: "username", + Password: "password", + Mechanism: "INVALID_MECHANISM", + }, + expectedError: fmt.Errorf("config plaintext.mechanism error: %s, only support 'SCRAM-SHA-256' or 'SCRAM-SHA-512' or 'PLAIN'", "INVALID_MECHANISM"), + }, + } + + for _, tt := range tests { + t.Run(tt.config.Mechanism, func(t *testing.T) { + saramaConfig := sarama.NewConfig() + + err := setPlainTextConfiguration(&tt.config, saramaConfig) + + if tt.expectedError != nil { + assert.EqualError(t, err, tt.expectedError.Error()) + } else { + require.NoError(t, err) + assert.True(t, saramaConfig.Net.SASL.Enable) + assert.Equal(t, tt.config.Username, saramaConfig.Net.SASL.User) + assert.Equal(t, tt.config.Password, saramaConfig.Net.SASL.Password) + assert.Equal(t, tt.expectedMechanism, saramaConfig.Net.SASL.Mechanism) + + if strings.HasPrefix(tt.config.Mechanism, "SCRAM-SHA-") { + assert.NotNil(t, saramaConfig.Net.SASL.SCRAMClientGeneratorFunc) + } else { + assert.Nil(t, saramaConfig.Net.SASL.SCRAMClientGeneratorFunc) + } + } + }) + } +} diff --git a/pkg/kafka/auth/tls_test.go b/pkg/kafka/auth/tls_test.go new file mode 100644 index 00000000000..d8dadf6cd64 --- /dev/null +++ b/pkg/kafka/auth/tls_test.go @@ -0,0 +1,28 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package auth + +import ( + "testing" + + "github.com/Shopify/sarama" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" + + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" +) + +func TestSetTLSConfiguration(t *testing.T) { + logger := zaptest.NewLogger(t) + saramaConfig := sarama.NewConfig() + tlsConfig := &tlscfg.Options{ + Enabled: true, + } + err := setTLSConfiguration(tlsConfig, saramaConfig, logger) + require.NoError(t, err) + assert.True(t, saramaConfig.Net.TLS.Enable) + assert.NotNil(t, saramaConfig.Net.TLS.Config) + defer tlsConfig.Close() +} diff --git a/pkg/kafka/consumer/config_test.go b/pkg/kafka/consumer/config_test.go new file mode 100644 index 00000000000..563fc9d6d1a --- /dev/null +++ b/pkg/kafka/consumer/config_test.go @@ -0,0 +1,20 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package consumer + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" + + "github.com/jaegertracing/jaeger/pkg/kafka/auth" +) + +func TestSetConfiguration(t *testing.T) { + logger := zaptest.NewLogger(t) + test := &Configuration{AuthenticationConfig: auth.AuthenticationConfig{Authentication: "fail"}} + _, err := test.NewConsumer(logger) + require.EqualError(t, err, "Unknown/Unsupported authentication method fail to kafka cluster") +} diff --git a/pkg/kafka/producer/config_test.go b/pkg/kafka/producer/config_test.go new file mode 100644 index 00000000000..87bd8631c69 --- /dev/null +++ b/pkg/kafka/producer/config_test.go @@ -0,0 +1,20 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package producer + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" + + "github.com/jaegertracing/jaeger/pkg/kafka/auth" +) + +func TestSetConfiguration(t *testing.T) { + logger := zaptest.NewLogger(t) + test := &Configuration{AuthenticationConfig: auth.AuthenticationConfig{Authentication: "fail"}} + _, err := test.NewProducer(logger) + require.EqualError(t, err, "Unknown/Unsupported authentication method fail to kafka cluster") +} diff --git a/pkg/telemetery/settings.go b/pkg/telemetery/settings.go new file mode 100644 index 00000000000..1cb9551a6b9 --- /dev/null +++ b/pkg/telemetery/settings.go @@ -0,0 +1,40 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package telemetery + +import ( + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/metrics" +) + +type Setting struct { + Logger *zap.Logger + TracerProvider trace.TracerProvider + Metrics metrics.Factory + ReportStatus func(*component.StatusEvent) +} + +func HCAdapter(hc *healthcheck.HealthCheck) func(*component.StatusEvent) { + return func(event *component.StatusEvent) { + var hcStatus healthcheck.Status + switch event.Status() { + case component.StatusOK: + hcStatus = healthcheck.Ready + case component.StatusStarting, + component.StatusRecoverableError, + component.StatusPermanentError, + component.StatusNone, + component.StatusStopping, + component.StatusStopped: + hcStatus = healthcheck.Unavailable + case component.StatusFatalError: + hcStatus = healthcheck.Broken + } + hc.Set(hcStatus) + } +} diff --git a/pkg/telemetery/settings_test.go b/pkg/telemetery/settings_test.go new file mode 100644 index 00000000000..bb82b61e57d --- /dev/null +++ b/pkg/telemetery/settings_test.go @@ -0,0 +1,78 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package telemetery_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/component" + + "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/telemetery" + "github.com/jaegertracing/jaeger/pkg/testutils" +) + +func TestHCAdapter(t *testing.T) { + tests := []struct { + name string + status component.Status + expectedHC healthcheck.Status + }{ + { + name: "StatusOK", + status: component.StatusOK, + expectedHC: healthcheck.Ready, + }, + { + name: "StatusStarting", + status: component.StatusStarting, + expectedHC: healthcheck.Unavailable, + }, + { + name: "StatusRecoverableError", + status: component.StatusRecoverableError, + expectedHC: healthcheck.Unavailable, + }, + { + name: "StatusPermanentError", + status: component.StatusPermanentError, + expectedHC: healthcheck.Unavailable, + }, + { + name: "StatusNone", + status: component.StatusNone, + expectedHC: healthcheck.Unavailable, + }, + { + name: "StatusStopping", + status: component.StatusStopping, + expectedHC: healthcheck.Unavailable, + }, + { + name: "StatusStopped", + status: component.StatusStopped, + expectedHC: healthcheck.Unavailable, + }, + { + name: "StatusFatalError", + status: component.StatusFatalError, + expectedHC: healthcheck.Broken, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hc := healthcheck.New() + hcAdapter := telemetery.HCAdapter(hc) + event := component.NewStatusEvent(tt.status) + hcAdapter(event) + assert.Equal(t, tt.expectedHC, hc.Get()) + }) + } +} + +func TestMain(m *testing.M) { + testutils.VerifyGoLeaks(m) +} diff --git a/plugin/metrics/prometheus/factory.go b/plugin/metrics/prometheus/factory.go index 60452061d67..a806942eb46 100644 --- a/plugin/metrics/prometheus/factory.go +++ b/plugin/metrics/prometheus/factory.go @@ -40,7 +40,7 @@ type Factory struct { func NewFactory() *Factory { return &Factory{ tracer: otel.GetTracerProvider(), - options: NewOptions("prometheus"), + options: NewOptions(), } } @@ -64,5 +64,5 @@ func (f *Factory) Initialize(logger *zap.Logger) error { // CreateMetricsReader implements storage.MetricsFactory. func (f *Factory) CreateMetricsReader() (metricsstore.Reader, error) { - return prometheusstore.NewMetricsReader(f.options.Primary.Configuration, f.logger, f.tracer) + return prometheusstore.NewMetricsReader(f.options.Configuration, f.logger, f.tracer) } diff --git a/plugin/metrics/prometheus/factory_test.go b/plugin/metrics/prometheus/factory_test.go index dc03edc2304..bd49eca32d5 100644 --- a/plugin/metrics/prometheus/factory_test.go +++ b/plugin/metrics/prometheus/factory_test.go @@ -34,14 +34,13 @@ func TestPrometheusFactory(t *testing.T) { f := NewFactory() require.NoError(t, f.Initialize(zap.NewNop())) assert.NotNil(t, f.logger) - assert.Equal(t, "prometheus", f.options.Primary.namespace) listener, err := net.Listen("tcp", "localhost:") require.NoError(t, err) assert.NotNil(t, listener) defer listener.Close() - f.options.Primary.ServerURL = "http://" + listener.Addr().String() + f.options.ServerURL = "http://" + listener.Addr().String() reader, err := f.CreateMetricsReader() require.NoError(t, err) @@ -50,11 +49,11 @@ func TestPrometheusFactory(t *testing.T) { func TestWithDefaultConfiguration(t *testing.T) { f := NewFactory() - assert.Equal(t, "http://localhost:9090", f.options.Primary.ServerURL) - assert.Equal(t, 30*time.Second, f.options.Primary.ConnectTimeout) + assert.Equal(t, "http://localhost:9090", f.options.ServerURL) + assert.Equal(t, 30*time.Second, f.options.ConnectTimeout) - assert.Empty(t, f.options.Primary.MetricNamespace) - assert.Equal(t, "ms", f.options.Primary.LatencyUnit) + assert.Empty(t, f.options.MetricNamespace) + assert.Equal(t, "ms", f.options.LatencyUnit) } func TestWithConfiguration(t *testing.T) { @@ -69,10 +68,10 @@ func TestWithConfiguration(t *testing.T) { }) require.NoError(t, err) f.InitFromViper(v, zap.NewNop()) - assert.Equal(t, "http://localhost:1234", f.options.Primary.ServerURL) - assert.Equal(t, 5*time.Second, f.options.Primary.ConnectTimeout) - assert.Equal(t, "test/test_file.txt", f.options.Primary.TokenFilePath) - assert.False(t, f.options.Primary.TokenOverrideFromContext) + assert.Equal(t, "http://localhost:1234", f.options.ServerURL) + assert.Equal(t, 5*time.Second, f.options.ConnectTimeout) + assert.Equal(t, "test/test_file.txt", f.options.TokenFilePath) + assert.False(t, f.options.TokenOverrideFromContext) }) t.Run("with space in token file path", func(t *testing.T) { f := NewFactory() @@ -82,7 +81,7 @@ func TestWithConfiguration(t *testing.T) { }) require.NoError(t, err) f.InitFromViper(v, zap.NewNop()) - assert.Equal(t, "test/ test file.txt", f.options.Primary.TokenFilePath) + assert.Equal(t, "test/ test file.txt", f.options.TokenFilePath) }) t.Run("with custom configuration of prometheus.query", func(t *testing.T) { f := NewFactory() @@ -93,8 +92,8 @@ func TestWithConfiguration(t *testing.T) { }) require.NoError(t, err) f.InitFromViper(v, zap.NewNop()) - assert.Equal(t, "mynamespace", f.options.Primary.MetricNamespace) - assert.Equal(t, "ms", f.options.Primary.LatencyUnit) + assert.Equal(t, "mynamespace", f.options.MetricNamespace) + assert.Equal(t, "ms", f.options.LatencyUnit) }) t.Run("with invalid prometheus.query.duration-unit", func(t *testing.T) { defer func() { @@ -110,7 +109,7 @@ func TestWithConfiguration(t *testing.T) { }) require.NoError(t, err) f.InitFromViper(v, zap.NewNop()) - require.Empty(t, f.options.Primary.LatencyUnit) + require.Empty(t, f.options.LatencyUnit) }) } diff --git a/plugin/metrics/prometheus/options.go b/plugin/metrics/prometheus/options.go index a9f8f98956b..80cf09e3070 100644 --- a/plugin/metrics/prometheus/options.go +++ b/plugin/metrics/prometheus/options.go @@ -27,6 +27,8 @@ import ( ) const ( + prefix = "prometheus" + suffixServerURL = ".server-url" suffixConnectTimeout = ".connect-timeout" suffixTokenFilePath = ".token-file" @@ -48,18 +50,13 @@ const ( defaultNormalizeDuration = false ) -type namespaceConfig struct { - config.Configuration `mapstructure:",squash"` - namespace string -} - // Options stores the configuration entries for this storage. type Options struct { - Primary namespaceConfig `mapstructure:",squash"` + config.Configuration `mapstructure:",squash"` } // NewOptions creates a new Options struct. -func NewOptions(primaryNamespace string) *Options { +func NewOptions() *Options { defaultConfig := config.Configuration{ ServerURL: defaultServerURL, ConnectTimeout: defaultConnectTimeout, @@ -71,75 +68,70 @@ func NewOptions(primaryNamespace string) *Options { } return &Options{ - Primary: namespaceConfig{ - Configuration: defaultConfig, - namespace: primaryNamespace, - }, + Configuration: defaultConfig, } } // AddFlags from this storage to the CLI. func (opt *Options) AddFlags(flagSet *flag.FlagSet) { - nsConfig := &opt.Primary - flagSet.String(nsConfig.namespace+suffixServerURL, defaultServerURL, + flagSet.String(prefix+suffixServerURL, defaultServerURL, "The Prometheus server's URL, must include the protocol scheme e.g. http://localhost:9090") - flagSet.Duration(nsConfig.namespace+suffixConnectTimeout, defaultConnectTimeout, + flagSet.Duration(prefix+suffixConnectTimeout, defaultConnectTimeout, "The period to wait for a connection to Prometheus when executing queries.") - flagSet.String(nsConfig.namespace+suffixTokenFilePath, defaultTokenFilePath, + flagSet.String(prefix+suffixTokenFilePath, defaultTokenFilePath, "The path to a file containing the bearer token which will be included when executing queries against the Prometheus API.") - flagSet.Bool(nsConfig.namespace+suffixOverrideFromContext, true, + flagSet.Bool(prefix+suffixOverrideFromContext, true, "Whether the bearer token should be overridden from context (incoming request)") - flagSet.String(nsConfig.namespace+suffixMetricNamespace, defaultMetricNamespace, + flagSet.String(prefix+suffixMetricNamespace, defaultMetricNamespace, `The metric namespace that is prefixed to the metric name. A '.' separator will be added between `+ `the namespace and the metric name.`) - flagSet.String(nsConfig.namespace+suffixLatencyUnit, defaultLatencyUnit, + flagSet.String(prefix+suffixLatencyUnit, defaultLatencyUnit, `The units used for the "latency" histogram. It can be either "ms" or "s" and should be consistent with the `+ `histogram unit value set in the spanmetrics connector (see: `+ `https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/connector/spanmetricsconnector#configurations). `+ `This also helps jaeger-query determine the metric name when querying for "latency" metrics.`) - flagSet.Bool(nsConfig.namespace+suffixNormalizeCalls, defaultNormalizeCalls, + flagSet.Bool(prefix+suffixNormalizeCalls, defaultNormalizeCalls, `Whether to normalize the "calls" metric name according to `+ `https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/translator/prometheus/README.md. `+ `For example: `+ `"calls" (not normalized) -> "calls_total" (normalized), `) - flagSet.Bool(nsConfig.namespace+suffixNormalizeDuration, defaultNormalizeDuration, + flagSet.Bool(prefix+suffixNormalizeDuration, defaultNormalizeDuration, `Whether to normalize the "duration" metric name according to `+ `https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/translator/prometheus/README.md. `+ `For example: `+ `"duration_bucket" (not normalized) -> "duration_milliseconds_bucket (normalized)"`) - nsConfig.getTLSFlagsConfig().AddFlags(flagSet) + opt.getTLSFlagsConfig().AddFlags(flagSet) } // InitFromViper initializes the options struct with values from Viper. func (opt *Options) InitFromViper(v *viper.Viper) error { - cfg := &opt.Primary - cfg.ServerURL = stripWhiteSpace(v.GetString(cfg.namespace + suffixServerURL)) - cfg.ConnectTimeout = v.GetDuration(cfg.namespace + suffixConnectTimeout) - cfg.TokenFilePath = v.GetString(cfg.namespace + suffixTokenFilePath) + opt.ServerURL = stripWhiteSpace(v.GetString(prefix + suffixServerURL)) + opt.ConnectTimeout = v.GetDuration(prefix + suffixConnectTimeout) + opt.TokenFilePath = v.GetString(prefix + suffixTokenFilePath) - cfg.MetricNamespace = v.GetString(cfg.namespace + suffixMetricNamespace) - cfg.LatencyUnit = v.GetString(cfg.namespace + suffixLatencyUnit) - cfg.NormalizeCalls = v.GetBool(cfg.namespace + suffixNormalizeCalls) - cfg.NormalizeDuration = v.GetBool(cfg.namespace + suffixNormalizeDuration) - cfg.TokenOverrideFromContext = v.GetBool(cfg.namespace + suffixOverrideFromContext) + opt.MetricNamespace = v.GetString(prefix + suffixMetricNamespace) + opt.LatencyUnit = v.GetString(prefix + suffixLatencyUnit) + opt.NormalizeCalls = v.GetBool(prefix + suffixNormalizeCalls) + opt.NormalizeDuration = v.GetBool(prefix + suffixNormalizeDuration) + opt.TokenOverrideFromContext = v.GetBool(prefix + suffixOverrideFromContext) isValidUnit := map[string]bool{"ms": true, "s": true} - if _, ok := isValidUnit[cfg.LatencyUnit]; !ok { - return fmt.Errorf(`duration-unit must be one of "ms" or "s", not %q`, cfg.LatencyUnit) + if _, ok := isValidUnit[opt.LatencyUnit]; !ok { + return fmt.Errorf(`duration-unit must be one of "ms" or "s", not %q`, opt.LatencyUnit) } var err error - cfg.TLS, err = cfg.getTLSFlagsConfig().InitFromViper(v) + opt.TLS, err = opt.getTLSFlagsConfig().InitFromViper(v) if err != nil { return fmt.Errorf("failed to process Prometheus TLS options: %w", err) } return nil } -func (config *namespaceConfig) getTLSFlagsConfig() tlscfg.ClientFlagsConfig { +func (*Options) getTLSFlagsConfig() tlscfg.ClientFlagsConfig { return tlscfg.ClientFlagsConfig{ - Prefix: config.namespace, + Prefix: prefix, } } diff --git a/plugin/sampling/strategyprovider/adaptive/aggregator.go b/plugin/sampling/strategyprovider/adaptive/aggregator.go index 97b8e69399c..5fe4fcd59dd 100644 --- a/plugin/sampling/strategyprovider/adaptive/aggregator.go +++ b/plugin/sampling/strategyprovider/adaptive/aggregator.go @@ -129,6 +129,23 @@ func (a *aggregator) RecordThroughput(service, operation string, samplerType spa } } +func RecordThroughput(agg samplingstrategy.Aggregator, span *span_model.Span, logger *zap.Logger) { + // TODO simply checking parentId to determine if a span is a root span is not sufficient. However, + // we can be sure that only a root span will have sampler tags. + if span.ParentSpanID() != span_model.NewSpanID(0) { + return + } + service := span.Process.ServiceName + if service == "" || span.OperationName == "" { + return + } + samplerType, samplerParam := span.GetSamplerParams(logger) + if samplerType == span_model.SamplerTypeUnrecognized { + return + } + agg.RecordThroughput(service, span.OperationName, samplerType, samplerParam) +} + func (a *aggregator) Start() { a.postAggregator.Start() diff --git a/plugin/sampling/strategyprovider/adaptive/aggregator_test.go b/plugin/sampling/strategyprovider/adaptive/aggregator_test.go index bd34c3cb016..9e4247635d4 100644 --- a/plugin/sampling/strategyprovider/adaptive/aggregator_test.go +++ b/plugin/sampling/strategyprovider/adaptive/aggregator_test.go @@ -155,3 +155,44 @@ func TestRecordThroughput(t *testing.T) { a.HandleRootSpan(span, logger) assert.EqualValues(t, 1, a.(*aggregator).currentThroughput["A"]["GET"].Count) } + +func TestRecordThroughputFunc(t *testing.T) { + metricsFactory := metricstest.NewFactory(0) + mockStorage := &mocks.Store{} + mockEP := &epmocks.ElectionParticipant{} + logger := zap.NewNop() + testOpts := Options{ + CalculationInterval: 1 * time.Second, + AggregationBuckets: 1, + BucketsForCalculation: 1, + } + + a, err := NewAggregator(testOpts, logger, metricsFactory, mockEP, mockStorage) + require.NoError(t, err) + + // Testing non-root span + span := &model.Span{References: []model.SpanRef{{SpanID: model.NewSpanID(1), RefType: model.ChildOf}}} + RecordThroughput(a, span, logger) + require.Empty(t, a.(*aggregator).currentThroughput) + + // Testing span with service name but no operation + span.References = []model.SpanRef{} + span.Process = &model.Process{ + ServiceName: "A", + } + RecordThroughput(a, span, logger) + require.Empty(t, a.(*aggregator).currentThroughput) + + // Testing span with service name and operation but no probabilistic sampling tags + span.OperationName = "GET" + RecordThroughput(a, span, logger) + require.Empty(t, a.(*aggregator).currentThroughput) + + // Testing span with service name, operation, and probabilistic sampling tags + span.Tags = model.KeyValues{ + model.String("sampler.type", "probabilistic"), + model.String("sampler.param", "0.001"), + } + RecordThroughput(a, span, logger) + assert.EqualValues(t, 1, a.(*aggregator).currentThroughput["A"]["GET"].Count) +} diff --git a/plugin/sampling/strategyprovider/adaptive/options.go b/plugin/sampling/strategyprovider/adaptive/options.go index 64288632dbf..4eb13361443 100644 --- a/plugin/sampling/strategyprovider/adaptive/options.go +++ b/plugin/sampling/strategyprovider/adaptive/options.go @@ -53,7 +53,7 @@ const ( type Options struct { // TargetSamplesPerSecond is the global target rate of samples per operation. // TODO implement manual overrides per service/operation. - TargetSamplesPerSecond float64 + TargetSamplesPerSecond float64 `mapstructure:"target_samples_per_second"` // DeltaTolerance is the acceptable amount of deviation between the observed and the desired (target) // throughput for an operation, expressed as a ratio. For example, the value of 0.3 (30% deviation) @@ -62,23 +62,23 @@ type Options struct { // in the PID Controller terminology) to the sampler in the application. // // Increase this to reduce the amount of fluctuation in the calculated probabilities. - DeltaTolerance float64 + DeltaTolerance float64 `mapstructure:"delta_tolerance"` // CalculationInterval determines how often new probabilities are calculated. E.g. if it is 1 minute, // new sampling probabilities are calculated once a minute and each bucket will contain 1 minute worth // of aggregated throughput data. - CalculationInterval time.Duration + CalculationInterval time.Duration `mapstructure:"calculation_interval"` // AggregationBuckets is the total number of aggregated throughput buckets kept in memory, ie. if // the CalculationInterval is 1 minute (each bucket contains 1 minute of thoughput data) and the // AggregationBuckets is 3, the adaptive sampling processor will keep at most 3 buckets in memory for // all operations. // TODO(wjang): Expand on why this is needed when BucketsForCalculation seems to suffice. - AggregationBuckets int + AggregationBuckets int `mapstructure:"aggregation_buckets"` // BucketsForCalculation determines how many previous buckets used in calculating the weighted QPS, // ie. if BucketsForCalculation is 1, only the most recent bucket will be used in calculating the weighted QPS. - BucketsForCalculation int + BucketsForCalculation int `mapstructure:"calculation_buckets"` // Delay is the amount of time to delay probability generation by, ie. if the CalculationInterval // is 1 minute, the number of buckets is 10, and the delay is 2 minutes, then at one time @@ -88,29 +88,45 @@ type Options struct { // during any 1 minute interval, the clients will be fetching new probabilities in a uniformly // distributed manner throughout the 1 minute window. By setting the delay to 2 minutes, we can // guarantee that all clients can use the latest calculated probabilities for at least 1 minute. - Delay time.Duration + Delay time.Duration `mapstructure:"calculation_delay"` // InitialSamplingProbability is the initial sampling probability for all new operations. - InitialSamplingProbability float64 + InitialSamplingProbability float64 `mapstructure:"initial_sampling_probability"` // MinSamplingProbability is the minimum sampling probability for all operations. ie. the calculated sampling // probability will be in the range [MinSamplingProbability, 1.0]. - MinSamplingProbability float64 + MinSamplingProbability float64 `mapstructure:"min_sampling_probability"` // MinSamplesPerSecond determines the min number of traces that are sampled per second. // For example, if the value is 0.01666666666 (one every minute), then the sampling processor will do // its best to sample at least one trace a minute for an operation. This is useful for low QPS operations // that may never be sampled by the probabilistic sampler. - MinSamplesPerSecond float64 + MinSamplesPerSecond float64 `mapstructure:"min_samples_per_second"` // LeaderLeaseRefreshInterval is the duration to sleep if this processor is elected leader before // attempting to renew the lease on the leader lock. NB. This should be less than FollowerLeaseRefreshInterval // to reduce lock thrashing. - LeaderLeaseRefreshInterval time.Duration + LeaderLeaseRefreshInterval time.Duration `mapstructure:"leader_lease_refresh_interval"` // FollowerLeaseRefreshInterval is the duration to sleep if this processor is a follower // (ie. failed to gain the leader lock). - FollowerLeaseRefreshInterval time.Duration + FollowerLeaseRefreshInterval time.Duration `mapstructure:"follower_lease_refresh_interval"` +} + +func DefaultOptions() Options { + return Options{ + TargetSamplesPerSecond: defaultTargetSamplesPerSecond, + DeltaTolerance: defaultDeltaTolerance, + BucketsForCalculation: defaultBucketsForCalculation, + CalculationInterval: defaultCalculationInterval, + AggregationBuckets: defaultAggregationBuckets, + Delay: defaultDelay, + InitialSamplingProbability: defaultInitialSamplingProbability, + MinSamplingProbability: defaultMinSamplingProbability, + MinSamplesPerSecond: defaultMinSamplesPerSecond, + LeaderLeaseRefreshInterval: defaultLeaderLeaseRefreshInterval, + FollowerLeaseRefreshInterval: defaultFollowerLeaseRefreshInterval, + } } // AddFlags adds flags for Options diff --git a/plugin/sampling/strategyprovider/adaptive/options_test.go b/plugin/sampling/strategyprovider/adaptive/options_test.go index 1ab64c0589c..e441ad1472b 100644 --- a/plugin/sampling/strategyprovider/adaptive/options_test.go +++ b/plugin/sampling/strategyprovider/adaptive/options_test.go @@ -43,3 +43,18 @@ func TestOptionsWithFlags(t *testing.T) { assert.Equal(t, time.Duration(5000000000), opts.LeaderLeaseRefreshInterval) assert.Equal(t, time.Duration(60000000000), opts.FollowerLeaseRefreshInterval) } + +func TestDefaultOptions(t *testing.T) { + options := DefaultOptions() + assert.Equal(t, float64(defaultTargetSamplesPerSecond), options.TargetSamplesPerSecond) + assert.Equal(t, defaultDeltaTolerance, options.DeltaTolerance) + assert.Equal(t, defaultBucketsForCalculation, options.BucketsForCalculation) + assert.Equal(t, defaultCalculationInterval, options.CalculationInterval) + assert.Equal(t, defaultAggregationBuckets, options.AggregationBuckets) + assert.Equal(t, defaultDelay, options.Delay) + assert.Equal(t, defaultInitialSamplingProbability, options.InitialSamplingProbability) + assert.Equal(t, defaultMinSamplingProbability, options.MinSamplingProbability) + assert.Equal(t, defaultMinSamplesPerSecond, options.MinSamplesPerSecond) + assert.Equal(t, defaultLeaderLeaseRefreshInterval, options.LeaderLeaseRefreshInterval) + assert.Equal(t, defaultFollowerLeaseRefreshInterval, options.FollowerLeaseRefreshInterval) +} diff --git a/plugin/storage/es/mappings/fixtures/jaeger-dependencies-6.json b/plugin/storage/es/mappings/fixtures/jaeger-dependencies-6.json new file mode 100644 index 00000000000..42659ac9d03 --- /dev/null +++ b/plugin/storage/es/mappings/fixtures/jaeger-dependencies-6.json @@ -0,0 +1,10 @@ +{ + "template": "*jaeger-dependencies-*", + "settings":{ + "index.number_of_shards": 3, + "index.number_of_replicas": 3, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true + }, + "mappings":{} +} diff --git a/plugin/storage/es/mappings/fixtures/jaeger-sampling-6.json b/plugin/storage/es/mappings/fixtures/jaeger-sampling-6.json new file mode 100644 index 00000000000..87304011017 --- /dev/null +++ b/plugin/storage/es/mappings/fixtures/jaeger-sampling-6.json @@ -0,0 +1,10 @@ +{ + "template": "*jaeger-sampling-*", + "settings":{ + "index.number_of_shards": 3, + "index.number_of_replicas": 3, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true + }, + "mappings":{} +} diff --git a/plugin/storage/es/mappings/fixtures/jaeger-service-6.json b/plugin/storage/es/mappings/fixtures/jaeger-service-6.json new file mode 100644 index 00000000000..9ba447775ed --- /dev/null +++ b/plugin/storage/es/mappings/fixtures/jaeger-service-6.json @@ -0,0 +1,49 @@ +{ + "template": "*jaeger-service-*", + "settings":{ + "index.number_of_shards": 3, + "index.number_of_replicas": 3, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true, + "index.mapper.dynamic":false + }, + "mappings":{ + "_default_":{ + "_all":{ + "enabled":false + }, + "dynamic_templates":[ + { + "span_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"tag.*" + } + }, + { + "process_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"process.tag.*" + } + } + ] + }, + "service":{ + "properties":{ + "serviceName":{ + "type":"keyword", + "ignore_above":256 + }, + "operationName":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } +} diff --git a/plugin/storage/es/mappings/fixtures/jaeger-span-6.json b/plugin/storage/es/mappings/fixtures/jaeger-span-6.json new file mode 100644 index 00000000000..e7a89878009 --- /dev/null +++ b/plugin/storage/es/mappings/fixtures/jaeger-span-6.json @@ -0,0 +1,165 @@ +{ + "template": "*jaeger-span-*", + "settings":{ + "index.number_of_shards": 3, + "index.number_of_replicas": 3, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true, + "index.mapper.dynamic":false + }, + "mappings":{ + "_default_":{ + "_all":{ + "enabled":false + }, + "dynamic_templates":[ + { + "span_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"tag.*" + } + }, + { + "process_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"process.tag.*" + } + } + ] + }, + "span":{ + "properties":{ + "traceID":{ + "type":"keyword", + "ignore_above":256 + }, + "parentSpanID":{ + "type":"keyword", + "ignore_above":256 + }, + "spanID":{ + "type":"keyword", + "ignore_above":256 + }, + "operationName":{ + "type":"keyword", + "ignore_above":256 + }, + "startTime":{ + "type":"long" + }, + "startTimeMillis":{ + "type":"date", + "format":"epoch_millis" + }, + "duration":{ + "type":"long" + }, + "flags":{ + "type":"integer" + }, + "logs":{ + "type":"nested", + "dynamic":false, + "properties":{ + "timestamp":{ + "type":"long" + }, + "fields":{ + "type":"nested", + "dynamic":false, + "properties":{ + "key":{ + "type":"keyword", + "ignore_above":256 + }, + "value":{ + "type":"keyword", + "ignore_above":256 + }, + "tagType":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } + }, + "process":{ + "properties":{ + "serviceName":{ + "type":"keyword", + "ignore_above":256 + }, + "tag":{ + "type":"object" + }, + "tags":{ + "type":"nested", + "dynamic":false, + "properties":{ + "key":{ + "type":"keyword", + "ignore_above":256 + }, + "value":{ + "type":"keyword", + "ignore_above":256 + }, + "tagType":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } + }, + "references":{ + "type":"nested", + "dynamic":false, + "properties":{ + "refType":{ + "type":"keyword", + "ignore_above":256 + }, + "traceID":{ + "type":"keyword", + "ignore_above":256 + }, + "spanID":{ + "type":"keyword", + "ignore_above":256 + } + } + }, + "tag":{ + "type":"object" + }, + "tags":{ + "type":"nested", + "dynamic":false, + "properties":{ + "key":{ + "type":"keyword", + "ignore_above":256 + }, + "value":{ + "type":"keyword", + "ignore_above":256 + }, + "tagType":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } + } + } +} diff --git a/plugin/storage/es/mappings/jaeger-dependencies-6.json b/plugin/storage/es/mappings/jaeger-dependencies-6.json new file mode 100644 index 00000000000..cdfa62d9547 --- /dev/null +++ b/plugin/storage/es/mappings/jaeger-dependencies-6.json @@ -0,0 +1,10 @@ +{ + "template": "*jaeger-dependencies-*", + "settings":{ + "index.number_of_shards": {{ .Shards }}, + "index.number_of_replicas": {{ .Replicas }}, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true + }, + "mappings":{} +} diff --git a/plugin/storage/es/mappings/jaeger-sampling-6.json b/plugin/storage/es/mappings/jaeger-sampling-6.json new file mode 100644 index 00000000000..458d490a357 --- /dev/null +++ b/plugin/storage/es/mappings/jaeger-sampling-6.json @@ -0,0 +1,10 @@ +{ + "template": "*jaeger-sampling-*", + "settings":{ + "index.number_of_shards": {{ .Shards }}, + "index.number_of_replicas": {{ .Replicas }}, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":false + }, + "mappings":{} +} diff --git a/plugin/storage/es/mappings/jaeger-service-6.json b/plugin/storage/es/mappings/jaeger-service-6.json new file mode 100644 index 00000000000..1adb8e0bf38 --- /dev/null +++ b/plugin/storage/es/mappings/jaeger-service-6.json @@ -0,0 +1,49 @@ +{ + "template": "*jaeger-service-*", + "settings":{ + "index.number_of_shards": {{ .Shards }}, + "index.number_of_replicas": {{ .Replicas }}, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true, + "index.mapper.dynamic":false + }, + "mappings":{ + "_default_":{ + "_all":{ + "enabled":false + }, + "dynamic_templates":[ + { + "span_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"tag.*" + } + }, + { + "process_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"process.tag.*" + } + } + ] + }, + "service":{ + "properties":{ + "serviceName":{ + "type":"keyword", + "ignore_above":256 + }, + "operationName":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } +} diff --git a/plugin/storage/es/mappings/jaeger-span-6.json b/plugin/storage/es/mappings/jaeger-span-6.json new file mode 100644 index 00000000000..3c73f923c4c --- /dev/null +++ b/plugin/storage/es/mappings/jaeger-span-6.json @@ -0,0 +1,165 @@ +{ + "template": "*jaeger-span-*", + "settings":{ + "index.number_of_shards": {{ .Shards }}, + "index.number_of_replicas": {{ .Replicas }}, + "index.mapping.nested_fields.limit":50, + "index.requests.cache.enable":true, + "index.mapper.dynamic":false + }, + "mappings":{ + "_default_":{ + "_all":{ + "enabled":false + }, + "dynamic_templates":[ + { + "span_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"tag.*" + } + }, + { + "process_tags_map":{ + "mapping":{ + "type":"keyword", + "ignore_above":256 + }, + "path_match":"process.tag.*" + } + } + ] + }, + "span":{ + "properties":{ + "traceID":{ + "type":"keyword", + "ignore_above":256 + }, + "parentSpanID":{ + "type":"keyword", + "ignore_above":256 + }, + "spanID":{ + "type":"keyword", + "ignore_above":256 + }, + "operationName":{ + "type":"keyword", + "ignore_above":256 + }, + "startTime":{ + "type":"long" + }, + "startTimeMillis":{ + "type":"date", + "format":"epoch_millis" + }, + "duration":{ + "type":"long" + }, + "flags":{ + "type":"integer" + }, + "logs":{ + "type":"nested", + "dynamic":false, + "properties":{ + "timestamp":{ + "type":"long" + }, + "fields":{ + "type":"nested", + "dynamic":false, + "properties":{ + "key":{ + "type":"keyword", + "ignore_above":256 + }, + "value":{ + "type":"keyword", + "ignore_above":256 + }, + "tagType":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } + }, + "process":{ + "properties":{ + "serviceName":{ + "type":"keyword", + "ignore_above":256 + }, + "tag":{ + "type":"object" + }, + "tags":{ + "type":"nested", + "dynamic":false, + "properties":{ + "key":{ + "type":"keyword", + "ignore_above":256 + }, + "value":{ + "type":"keyword", + "ignore_above":256 + }, + "tagType":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } + }, + "references":{ + "type":"nested", + "dynamic":false, + "properties":{ + "refType":{ + "type":"keyword", + "ignore_above":256 + }, + "traceID":{ + "type":"keyword", + "ignore_above":256 + }, + "spanID":{ + "type":"keyword", + "ignore_above":256 + } + } + }, + "tag":{ + "type":"object" + }, + "tags":{ + "type":"nested", + "dynamic":false, + "properties":{ + "key":{ + "type":"keyword", + "ignore_above":256 + }, + "value":{ + "type":"keyword", + "ignore_above":256 + }, + "tagType":{ + "type":"keyword", + "ignore_above":256 + } + } + } + } + } + } +} diff --git a/plugin/storage/es/mappings/mapping.go b/plugin/storage/es/mappings/mapping.go index deee2a1cba4..d5da6ccb97f 100644 --- a/plugin/storage/es/mappings/mapping.go +++ b/plugin/storage/es/mappings/mapping.go @@ -46,8 +46,10 @@ type MappingBuilder struct { func (mb *MappingBuilder) GetMapping(mapping string) (string, error) { if mb.EsVersion == 8 { return mb.fixMapping(mapping + "-8.json") + } else if mb.EsVersion == 7 { + return mb.fixMapping(mapping + "-7.json") } - return mb.fixMapping(mapping + "-7.json") + return mb.fixMapping(mapping + "-6.json") } // GetSpanServiceMappings returns span and service mappings diff --git a/plugin/storage/es/mappings/mapping_test.go b/plugin/storage/es/mappings/mapping_test.go index efa56c335bf..a3b35f1dcca 100644 --- a/plugin/storage/es/mappings/mapping_test.go +++ b/plugin/storage/es/mappings/mapping_test.go @@ -35,17 +35,25 @@ import ( //go:embed fixtures/*.json var FIXTURES embed.FS -func TestMappingBuilder_GetMapping(t *testing.T) { +func TestMappingBuilderGetMapping(t *testing.T) { + const ( + jaegerSpan = "jaeger-span" + jaegerService = "jaeger-service" + jaegerDependencies = "jaeger-dependencies" + ) tests := []struct { mapping string esVersion uint }{ - {mapping: "jaeger-span", esVersion: 8}, - {mapping: "jaeger-span", esVersion: 7}, - {mapping: "jaeger-service", esVersion: 8}, - {mapping: "jaeger-service", esVersion: 7}, - {mapping: "jaeger-dependencies", esVersion: 8}, - {mapping: "jaeger-dependencies", esVersion: 7}, + {mapping: jaegerSpan, esVersion: 8}, + {mapping: jaegerSpan, esVersion: 7}, + {mapping: jaegerSpan, esVersion: 6}, + {mapping: jaegerService, esVersion: 8}, + {mapping: jaegerService, esVersion: 7}, + {mapping: jaegerService, esVersion: 6}, + {mapping: jaegerDependencies, esVersion: 8}, + {mapping: jaegerDependencies, esVersion: 7}, + {mapping: jaegerDependencies, esVersion: 6}, } for _, tt := range tests { t.Run(tt.mapping, func(t *testing.T) { @@ -73,14 +81,17 @@ func TestMappingBuilder_GetMapping(t *testing.T) { } } -func TestMappingBuilder_loadMapping(t *testing.T) { +func TestMappingBuilderLoadMapping(t *testing.T) { tests := []struct { name string }{ + {name: "jaeger-span-6.json"}, {name: "jaeger-span-7.json"}, {name: "jaeger-span-8.json"}, + {name: "jaeger-service-6.json"}, {name: "jaeger-service-7.json"}, {name: "jaeger-service-8.json"}, + {name: "jaeger-dependencies-6.json"}, {name: "jaeger-dependencies-7.json"}, {name: "jaeger-dependencies-8.json"}, } @@ -96,7 +107,7 @@ func TestMappingBuilder_loadMapping(t *testing.T) { } } -func TestMappingBuilder_fixMapping(t *testing.T) { +func TestMappingBuilderFixMapping(t *testing.T) { tests := []struct { name string templateBuilderMockFunc func() *mocks.TemplateBuilder @@ -156,7 +167,7 @@ func TestMappingBuilder_fixMapping(t *testing.T) { } } -func TestMappingBuilder_GetSpanServiceMappings(t *testing.T) { +func TestMappingBuilderGetSpanServiceMappings(t *testing.T) { type args struct { shards int64 replicas int64 @@ -210,6 +221,65 @@ func TestMappingBuilder_GetSpanServiceMappings(t *testing.T) { }, err: "template load error", }, + + { + name: "ES Version < 7", + args: args{ + shards: 3, + replicas: 3, + esVersion: 6, + indexPrefix: "test", + useILM: true, + ilmPolicyName: "jaeger-test-policy", + }, + mockNewTextTemplateBuilder: func() es.TemplateBuilder { + tb := mocks.TemplateBuilder{} + ta := mocks.TemplateApplier{} + ta.On("Execute", mock.Anything, mock.Anything).Return(nil) + tb.On("Parse", mock.Anything).Return(&ta, nil) + return &tb + }, + err: "", + }, + { + name: "ES Version < 7 Service Error", + args: args{ + shards: 3, + replicas: 3, + esVersion: 6, + indexPrefix: "test", + useILM: true, + ilmPolicyName: "jaeger-test-policy", + }, + mockNewTextTemplateBuilder: func() es.TemplateBuilder { + tb := mocks.TemplateBuilder{} + ta := mocks.TemplateApplier{} + ta.On("Execute", mock.Anything, mock.Anything).Return(nil).Once() + ta.On("Execute", mock.Anything, mock.Anything).Return(errors.New("template load error")).Once() + tb.On("Parse", mock.Anything).Return(&ta, nil) + return &tb + }, + err: "template load error", + }, + { + name: "ES Version < 7 Span Error", + args: args{ + shards: 3, + replicas: 3, + esVersion: 6, + indexPrefix: "test", + useILM: true, + ilmPolicyName: "jaeger-test-policy", + }, + mockNewTextTemplateBuilder: func() es.TemplateBuilder { + tb := mocks.TemplateBuilder{} + ta := mocks.TemplateApplier{} + ta.On("Execute", mock.Anything, mock.Anything).Return(errors.New("template load error")) + tb.On("Parse", mock.Anything).Return(&ta, nil) + return &tb + }, + err: "template load error", + }, { name: "ES Version 7 Span Error", args: args{ @@ -251,7 +321,7 @@ func TestMappingBuilder_GetSpanServiceMappings(t *testing.T) { } } -func TestMappingBuilder_GetDependenciesMappings(t *testing.T) { +func TestMappingBuilderGetDependenciesMappings(t *testing.T) { tb := mocks.TemplateBuilder{} ta := mocks.TemplateApplier{} ta.On("Execute", mock.Anything, mock.Anything).Return(errors.New("template load error")) @@ -264,7 +334,7 @@ func TestMappingBuilder_GetDependenciesMappings(t *testing.T) { require.EqualError(t, err, "template load error") } -func TestMappingBuilder_GetSamplingMappings(t *testing.T) { +func TestMappingBuilderGetSamplingMappings(t *testing.T) { tb := mocks.TemplateBuilder{} ta := mocks.TemplateApplier{} ta.On("Execute", mock.Anything, mock.Anything).Return(errors.New("template load error")) diff --git a/plugin/storage/factory.go b/plugin/storage/factory.go index 4e6de6d29be..433111e349b 100644 --- a/plugin/storage/factory.go +++ b/plugin/storage/factory.go @@ -46,7 +46,6 @@ const ( memoryStorageType = "memory" kafkaStorageType = "kafka" grpcStorageType = "grpc" - grpcPluginDeprecated = "grpc-plugin" badgerStorageType = "badger" blackholeStorageType = "blackhole" diff --git a/plugin/storage/factory_config.go b/plugin/storage/factory_config.go index 1d8a0748d8f..f58af121ca4 100644 --- a/plugin/storage/factory_config.go +++ b/plugin/storage/factory_config.go @@ -66,13 +66,6 @@ func FactoryConfigFromEnvAndCLI(args []string, log io.Writer) FactoryConfig { if spanStorageType == "" { spanStorageType = cassandraStorageType } - if spanStorageType == grpcPluginDeprecated { - fmt.Fprintf(log, - "WARNING: `%s` storage type is deprecated and will be remove from v1.60. Use `%s`.\n\n", - grpcPluginDeprecated, grpcStorageType, - ) - spanStorageType = grpcStorageType - } spanWriterTypes := strings.Split(spanStorageType, ",") if len(spanWriterTypes) > 1 { fmt.Fprintf(log, diff --git a/plugin/storage/integration/elasticsearch_test.go b/plugin/storage/integration/elasticsearch_test.go index 3f36334f7f9..eebed03dbc6 100644 --- a/plugin/storage/integration/elasticsearch_test.go +++ b/plugin/storage/integration/elasticsearch_test.go @@ -201,7 +201,7 @@ func TestElasticsearchStorage_IndexTemplates(t *testing.T) { esVersion, err := s.getVersion() require.NoError(t, err) // TODO abstract this into pkg/es/client.IndexManagementLifecycleAPI - if esVersion == 7 { + if esVersion == 6 || esVersion == 7 { serviceTemplateExists, err := s.client.IndexTemplateExists(indexPrefix + "-jaeger-service").Do(context.Background()) require.NoError(t, err) assert.True(t, serviceTemplateExists) diff --git a/plugin/storage/integration/es_index_cleaner_test.go b/plugin/storage/integration/es_index_cleaner_test.go index d9bc35bd67d..30ad3ff6bee 100644 --- a/plugin/storage/integration/es_index_cleaner_test.go +++ b/plugin/storage/integration/es_index_cleaner_test.go @@ -32,8 +32,8 @@ const ( samplingIndexName = "jaeger-sampling-2019-01-01" spanIndexName = "jaeger-span-2019-01-01" serviceIndexName = "jaeger-service-2019-01-01" - indexCleanerImage = "jaegertracing/jaeger-es-index-cleaner:latest" - rolloverImage = "jaegertracing/jaeger-es-rollover:latest" + indexCleanerImage = "localhost:5000/jaegertracing/jaeger-es-index-cleaner:local-test" + rolloverImage = "localhost:5000/jaegertracing/jaeger-es-rollover:local-test" rolloverNowEnvVar = `CONDITIONS='{"max_age":"0s"}'` ) diff --git a/plugin/storage/integration/remote_memory_storage.go b/plugin/storage/integration/remote_memory_storage.go index 1bd2dd7b3e5..7ac8d90551f 100644 --- a/plugin/storage/integration/remote_memory_storage.go +++ b/plugin/storage/integration/remote_memory_storage.go @@ -20,6 +20,7 @@ import ( "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/healthcheck" "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/telemetery" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/plugin/storage" "github.com/jaegertracing/jaeger/ports" @@ -47,7 +48,11 @@ func StartNewRemoteMemoryStorage(t *testing.T) *RemoteMemoryStorage { require.NoError(t, storageFactory.Initialize(metrics.NullFactory, logger)) t.Logf("Starting in-process remote storage server on %s", opts.GRPCHostPort) - server, err := app.NewServer(opts, storageFactory, tm, logger, healthcheck.New()) + telset := telemetery.Setting{ + Logger: logger, + ReportStatus: telemetery.HCAdapter(healthcheck.New()), + } + server, err := app.NewServer(opts, storageFactory, tm, telset) require.NoError(t, err) require.NoError(t, server.Start()) diff --git a/plugin/storage/memory/factory.go b/plugin/storage/memory/factory.go index 6c677b03d95..d9bfd6c113a 100644 --- a/plugin/storage/memory/factory.go +++ b/plugin/storage/memory/factory.go @@ -16,6 +16,7 @@ package memory import ( + "context" "flag" "github.com/spf13/viper" @@ -36,6 +37,7 @@ var ( // interface comformance checks _ storage.ArchiveFactory = (*Factory)(nil) _ storage.SamplingStoreFactory = (*Factory)(nil) _ plugin.Configurable = (*Factory)(nil) + _ storage.Purger = (*Factory)(nil) ) // Factory implements storage.Factory and creates storage components backed by memory store. @@ -126,3 +128,10 @@ func (*Factory) CreateLock() (distributedlock.Lock, error) { func (f *Factory) publishOpts() { safeexpvar.SetInt("jaeger_storage_memory_max_traces", int64(f.options.Configuration.MaxTraces)) } + +// Purge removes all data from the Factory's underlying Memory store. +// This function is intended for testing purposes only and should not be used in production environments. +func (f *Factory) Purge(ctx context.Context) error { + f.store.purge(ctx) + return nil +} diff --git a/plugin/storage/memory/memory.go b/plugin/storage/memory/memory.go index f92ae9949e9..9bac7d2b2bd 100644 --- a/plugin/storage/memory/memory.go +++ b/plugin/storage/memory/memory.go @@ -337,3 +337,10 @@ func flattenTags(span *model.Span) model.KeyValues { } return retMe } + +// purge supports Purger interface. +func (st *Store) purge(context.Context) { + st.Lock() + st.perTenant = make(map[string]*Tenant) + st.Unlock() +} diff --git a/plugin/storage/scylladb/docker-compose.yml b/plugin/storage/scylladb/docker-compose.yml index eecfb83fbba..2e5a69c2a44 100644 --- a/plugin/storage/scylladb/docker-compose.yml +++ b/plugin/storage/scylladb/docker-compose.yml @@ -1,10 +1,16 @@ +# docker compose file to test Scylla with Jaeger. + +# Disclaimer: This defaults to using 'latest' image tag for Jaeger images, +# which can be stale in your local repository. In case of issues try running +# against the actual Jaeger version like JAEGER_VERSION=1.59.0. + networks: jaeger-scylladb: services: collector: restart: unless-stopped - image: jaegertracing/jaeger-collector:1.58 + image: jaegertracing/jaeger-collector:${JAEGER_VERSION:-latest} environment: SPAN_STORAGE_TYPE: cassandra CASSANDRA_SERVERS: scylladb @@ -15,7 +21,7 @@ services: - cassandra-schema web: - image: jaegertracing/jaeger-query:1.58 + image: jaegertracing/jaeger-query:${JAEGER_VERSION:-latest} restart: unless-stopped ports: - 16686:16686 @@ -30,7 +36,7 @@ services: - cassandra-schema cassandra-schema: - image: jaegertracing/jaeger-cassandra-schema:1.58 + image: jaegertracing/jaeger-cassandra-schema:${JAEGER_VERSION:-latest} environment: CASSANDRA_PROTOCOL_VERSION: 4 CASSANDRA_VERSION: 4 @@ -77,7 +83,7 @@ services: - jaeger-scylladb hotrod: - image: jaegertracing/example-hotrod:1.58 + image: jaegertracing/example-hotrod:${JAEGER_VERSION:-latest} ports: - 8080:8080 command: [ "all" ] diff --git a/ports/ports.go b/ports/ports.go index caf035fc761..7e1fed56125 100644 --- a/ports/ports.go +++ b/ports/ports.go @@ -61,15 +61,6 @@ func PortToHostPort(port int) string { return ":" + strconv.Itoa(port) } -// GetAddressFromCLIOptions gets listening address based on port (deprecated flags) or host:port (new flags) -func GetAddressFromCLIOptions(port int, hostPort string) string { - if port != 0 { - return PortToHostPort(port) - } - - return FormatHostPort(hostPort) -} - // FormatHostPort returns hostPort in a usable format (host:port) if it wasn't already func FormatHostPort(hostPort string) string { if hostPort == "" { diff --git a/ports/ports_test.go b/ports/ports_test.go index a6dae7a4f18..6aed1f872d2 100644 --- a/ports/ports_test.go +++ b/ports/ports_test.go @@ -15,7 +15,6 @@ package ports import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -27,26 +26,6 @@ func TestPortToHostPort(t *testing.T) { assert.Equal(t, ":42", PortToHostPort(42)) } -func TestGetAddressFromCLIOptionsLegacy(t *testing.T) { - tests := []struct { - port int - hostPort string - out string - }{ - {port: 123, hostPort: "", out: ":123"}, - {port: 0, hostPort: "127.0.0.1:123", out: "127.0.0.1:123"}, - {port: 0, hostPort: ":123", out: ":123"}, - {port: 123, hostPort: "567", out: ":123"}, - {port: 0, hostPort: "123", out: ":123"}, - {port: 0, hostPort: "", out: ""}, - } - for _, test := range tests { - t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) { - assert.Equal(t, test.out, GetAddressFromCLIOptions(test.port, test.hostPort)) - }) - } -} - func TestFormatHostPort(t *testing.T) { assert.Equal(t, ":42", FormatHostPort("42")) assert.Equal(t, ":831", FormatHostPort(":831")) diff --git a/renovate.json b/renovate.json index b4913b64c9c..ef7a5b20f83 100644 --- a/renovate.json +++ b/renovate.json @@ -17,6 +17,7 @@ "matchFileNames": [ "docker-compose/cassandra/v3/docker-compose.yaml", "docker-compose/cassandra/v4/docker-compose.yaml", + "docker-compose/elasticsearch/v6/docker-compose.yml", "docker-compose/elasticsearch/v7/docker-compose.yml", "docker-compose/elasticsearch/v8/docker-compose.yml", "docker-compose/kafka-integration-test/docker-compose.yml", @@ -31,7 +32,13 @@ }, { "matchManagers": ["github-actions"], - "groupName": "github-actions deps" + "groupName": "github-actions deps", + "schedule": ["on the first day of the month"] + }, + { + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["patch", "digest"], + "enabled": false }, { "matchManagers": ["gomod"], diff --git a/scripts/adaptive-sampling-integration-test.sh b/scripts/adaptive-sampling-integration-test.sh new file mode 100644 index 00000000000..99be9e59fd1 --- /dev/null +++ b/scripts/adaptive-sampling-integration-test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -euf -o pipefail + +# This script is currently a placeholder. + +# Commands to run integration test: +# SAMPLING_STORAGE_TYPE=memory SAMPLING_CONFIG_TYPE=adaptive go run -tags=ui ./cmd/all-in-one --log-level=debug +# go run ./cmd/tracegen -adaptive-sampling=http://localhost:14268/api/sampling -pause=10ms -duration=60m + +# Check how strategy is changing +# curl 'http://localhost:14268/api/sampling?service=tracegen' | jq . + +# Issues +# - SDK does not report sampling probability in the tags the way Jaeger SDKs did +# - Server probably does not recognize spans as having adaptive sampling without sampler info +# - There is no way to modify target traces-per-second dynamically, must restart collector. diff --git a/scripts/compute-tags.sh b/scripts/compute-tags.sh index abb11bde10d..2feec3c850c 100644 --- a/scripts/compute-tags.sh +++ b/scripts/compute-tags.sh @@ -22,7 +22,6 @@ fi # for docker.io and quay.io BUILD_IMAGE=${BUILD_IMAGE:-"${BASE_BUILD_IMAGE}:${MAJOR_MINOR_PATCH}"} IMAGE_TAGS="--tag docker.io/${BASE_BUILD_IMAGE} --tag docker.io/${BUILD_IMAGE} --tag quay.io/${BASE_BUILD_IMAGE} --tag quay.io/${BUILD_IMAGE}" -SNAPSHOT_TAG="${BASE_BUILD_IMAGE}-snapshot:${GITHUB_SHA}" if [ "${MAJOR_MINOR}x" != "x" ]; then MAJOR_MINOR_IMAGE="${BASE_BUILD_IMAGE}:${MAJOR_MINOR}" @@ -34,6 +33,9 @@ if [ "${MAJOR}x" != "x" ]; then IMAGE_TAGS="${IMAGE_TAGS} --tag docker.io/${MAJOR_IMAGE} --tag quay.io/${MAJOR_IMAGE}" fi -IMAGE_TAGS="${IMAGE_TAGS} --tag docker.io/${SNAPSHOT_TAG} --tag quay.io/${SNAPSHOT_TAG}" +SNAPSHOT_TAG1="${BASE_BUILD_IMAGE}-snapshot:${GITHUB_SHA}" +IMAGE_TAGS="${IMAGE_TAGS} --tag docker.io/${SNAPSHOT_TAG1} --tag quay.io/${SNAPSHOT_TAG1}" +SNAPSHOT_TAG2="${BASE_BUILD_IMAGE}-snapshot:latest" +IMAGE_TAGS="${IMAGE_TAGS} --tag docker.io/${SNAPSHOT_TAG2} --tag quay.io/${SNAPSHOT_TAG2}" echo "${IMAGE_TAGS}" diff --git a/scripts/es-integration-test.sh b/scripts/es-integration-test.sh index 9b1ecf6e794..a9d98d9798e 100755 --- a/scripts/es-integration-test.sh +++ b/scripts/es-integration-test.sh @@ -1,19 +1,22 @@ #!/bin/bash PS4='T$(date "+%H:%M:%S") ' -set -euxf -o pipefail +set -euf -o pipefail # use global variables to reflect status of db db_is_up= usage() { - echo $"Usage: $0 " + echo "Usage: $0 " + echo " backend: elasticsearch | opensearch" + echo " backend_version: major version, e.g. 7.x" + echo " jaeger_version: major version, e.g. v1 | v2" exit 1 } check_arg() { if [ ! $# -eq 3 ]; then - echo "ERROR: need exactly three arguments, " + echo "ERROR: need exactly three arguments" usage fi } @@ -96,20 +99,34 @@ teardown_storage() { docker compose -f "${compose_file}" down } +build_local_img(){ + make build-es-index-cleaner GOOS=linux + make build-es-rollover GOOS=linux + make create-baseimg PLATFORMS="linux/$(go env GOARCH)" + #build es-index-cleaner and es-rollover images + GITHUB_SHA=local-test BRANCH=local-test bash scripts/build-upload-a-docker-image.sh -l -b -c jaeger-es-index-cleaner -d cmd/es-index-cleaner -t release -p "linux/$(go env GOARCH)" + GITHUB_SHA=local-test BRANCH=local-test bash scripts/build-upload-a-docker-image.sh -l -b -c jaeger-es-rollover -d cmd/es-rollover -t release -p "linux/$(go env GOARCH)" +} + main() { check_arg "$@" local distro=$1 local es_version=$2 - local j_version=$2 + local j_version=$3 - bring_up_storage "${distro}" "${es_version}" + set -x + bring_up_storage "${distro}" "${es_version}" + build_local_img if [[ "${j_version}" == "v2" ]]; then STORAGE=${distro} SPAN_STORAGE_TYPE=${distro} make jaeger-v2-storage-integration-test - else + elif [[ "${j_version}" == "v1" ]]; then STORAGE=${distro} make storage-integration-test make index-cleaner-integration-test make index-rollover-integration-test + else + echo "ERROR: Invalid argument value jaeger_version=${j_version}, expecing v1/v2". + exit 1 fi } diff --git a/scripts/hotrod-integration-test.sh b/scripts/hotrod-integration-test.sh index 40149bef8ff..2cb8d71266a 100755 --- a/scripts/hotrod-integration-test.sh +++ b/scripts/hotrod-integration-test.sh @@ -2,32 +2,89 @@ set -euxf -o pipefail +docker_compose_file="./examples/hotrod/docker-compose.yml" +platforms="linux/amd64,linux/s390x,linux/ppc64le,linux/arm64" + +teardown() { + echo "Tearing down..." + docker compose -f "$docker_compose_file" down +} +trap teardown EXIT + make build-examples GOOS=linux GOARCH=amd64 make build-examples GOOS=linux GOARCH=s390x make build-examples GOOS=linux GOARCH=ppc64le make build-examples GOOS=linux GOARCH=arm64 -REPO=jaegertracing/example-hotrod -platforms="linux/amd64,linux/s390x,linux/ppc64le,linux/arm64" make prepare-docker-buildx +make create-baseimg + +# Loop through each platform (separated by commas) +for platform in $(echo "$platforms" | tr ',' ' '); do + # Extract the architecture from the platform string + arch=${platform##*/} # Remove everything before the last slash + make "build-all-in-one" GOOS=linux GOARCH="${arch}" +done -# build image locally (-l) for integration test +# Build image locally (-l) for integration test bash scripts/build-upload-a-docker-image.sh -l -c example-hotrod -d examples/hotrod -p "${platforms}" +bash scripts/build-upload-a-docker-image.sh -l -b -c all-in-one -d cmd/all-in-one -p "${platforms}" -t release -# pass --name example-hotrod so that we can do `docker logs example-hotrod` later -export CID -CID=$(docker run -d --name example-hotrod -p 8080:8080 "localhost:5000/${REPO}:${GITHUB_SHA}") +JAEGER_VERSION=$GITHUB_SHA REGISTRY="localhost:5000/" docker compose -f "$docker_compose_file" up -d i=0 -while [[ "$(curl -s -o /dev/null -w '%{http_code}' localhost:8080)" != "200" && ${i} -lt 30 ]]; do +while [[ "$(curl -s -o /dev/null -w '%{http_code}' localhost:8080)" != "200" && $i -lt 30 ]]; do sleep 1 i=$((i+1)) done + body=$(curl localhost:8080) if [[ $body != *"Rides On Demand"* ]]; then echo "String \"Rides On Demand\" is not present on the index page" exit 1 fi -docker rm -f "$CID" +response=$(curl -i -X POST "http://localhost:8080/dispatch?customer=123") +TRACE_ID=$(echo "$response" | grep -Fi "Traceresponse:" | awk '{print $2}' | cut -d '-' -f 2) + +if [ -n "$TRACE_ID" ]; then + echo "TRACE_ID is not empty: $TRACE_ID" +else + echo "TRACE_ID is empty" + exit 1 +fi + +JAEGER_QUERY_URL="http://localhost:16686" +EXPECTED_SPANS=35 +MAX_RETRIES=30 +SLEEP_INTERVAL=10 + +# Function to poll Jaeger for the trace +poll_jaeger() { + local trace_id=$1 + local url="${JAEGER_QUERY_URL}/api/traces/${trace_id}" + + curl -s "${url}" | jq '.data[0].spans | length' || echo "0" +} + +# Polling loop +span_count=0 +for ((i=1; i<=MAX_RETRIES; i++)); do + span_count=$(poll_jaeger "${TRACE_ID}") + + if [[ "$span_count" -ge "$EXPECTED_SPANS" ]]; then + echo "Trace found with $span_count spans." + break + fi + + echo "Retry $i/$MAX_RETRIES: Trace not found or insufficient spans ($span_count/$EXPECTED_SPANS). Retrying in $SLEEP_INTERVAL seconds..." + sleep $SLEEP_INTERVAL +done + +if [[ "$span_count" -lt "$EXPECTED_SPANS" ]]; then + echo "Failed to find the trace with the expected number of spans within the timeout period." + exit 1 +fi + +# Ensure the image is published after successful test (no -l flag) bash scripts/build-upload-a-docker-image.sh -c example-hotrod -d examples/hotrod -p "${platforms}" diff --git a/scripts/spm-integration-test.sh b/scripts/spm-integration-test.sh index a0270586130..61e99911206 100755 --- a/scripts/spm-integration-test.sh +++ b/scripts/spm-integration-test.sh @@ -61,17 +61,24 @@ validate_service_metrics() { # Store the values in an array mapfile -t metric_points < <(echo "$response" | jq -r '.metrics[0].metricPoints[].gaugeValue.doubleValue') echo "Metric datapoints found for service '$service': " "${metric_points[@]}" - # Check that all values are non-zero + # Check that atleast some values are non-zero after the threshold local non_zero_count=0 + local expected_non_zero_count=3 + local zero_count=0 + local expected_max_zero_count=3 for value in "${metric_points[@]}"; do if [[ $(echo "$value > 0.0" | bc) == "1" ]]; then non_zero_count=$((non_zero_count + 1)) else - echo "❌ ERROR: Zero values not expected" + zero_count=$((zero_count + 1)) + fi + + if [[ $zero_count -gt $expected_max_zero_count ]]; then + echo "❌ ERROR: Zero values crossing threshold limit not expected (Threshold limit - '$expected_max_zero_count')" return 1 fi done - if [ $non_zero_count -lt 3 ]; then + if [ $non_zero_count -lt $expected_non_zero_count ]; then echo "⏳ Expecting at least 3 non-zero data points" return 1 fi