diff --git a/.github/workflows/ci-all-in-one-build.yml b/.github/workflows/ci-all-in-one-build.yml index 6df36c9185a..f342ebc83f1 100644 --- a/.github/workflows/ci-all-in-one-build.yml +++ b/.github/workflows/ci-all-in-one-build.yml @@ -11,7 +11,12 @@ jobs: all-in-one: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -19,13 +24,13 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - - uses: actions/setup-node@v3 + - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 with: - node-version: '10' + node-version: '16' - name: Export BRANCH variable uses: ./.github/actions/setup-branch @@ -33,7 +38,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 - name: Build, test, and publish all-in-one image run: bash scripts/build-all-in-one-image.sh diff --git a/.github/workflows/ci-build-binaries.yml b/.github/workflows/ci-build-binaries.yml index 5ac01fa3c08..d6600d373e9 100644 --- a/.github/workflows/ci-build-binaries.yml +++ b/.github/workflows/ci-build-binaries.yml @@ -29,7 +29,12 @@ jobs: # task: build-binaries-ppc64le name: build binaries for ${{ matrix.platform.name }} steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -37,9 +42,9 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Export BRANCH variable uses: ./.github/actions/setup-branch diff --git a/.github/workflows/ci-cassandra.yml b/.github/workflows/ci-cassandra.yml index b16008dc9f3..5401e6ec8b4 100644 --- a/.github/workflows/ci-cassandra.yml +++ b/.github/workflows/ci-cassandra.yml @@ -7,6 +7,9 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: cassandra: runs-on: ubuntu-latest @@ -23,11 +26,16 @@ jobs: schema: v004 name: ${{ matrix.version.distribution }} ${{ matrix.version.major }} steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Run cassandra integration tests run: bash scripts/cassandra-integration-test.sh ${{ matrix.version.image }} ${{ matrix.version.schema }} diff --git a/.github/workflows/ci-crossdock.yml b/.github/workflows/ci-crossdock.yml index 6f620d90466..60c83fa8d49 100644 --- a/.github/workflows/ci-crossdock.yml +++ b/.github/workflows/ci-crossdock.yml @@ -10,14 +10,14 @@ on: jobs: crossdock: runs-on: ubuntu-latest - strategy: - matrix: - steps: - - name: crossdock - cmd: bash scripts/build-crossdock.sh - name: ${{ matrix.steps.name }} + steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -25,20 +25,20 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Export BRANCH variable uses: ./.github/actions/setup-branch - name: Install tools run: make install-ci - - - uses: docker/setup-qemu-action@v2 - - name: Build, test, and publish ${{ matrix.steps.name }} image - run: ${{ matrix.steps.cmd }} + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 + + - name: Build, test, and publish crossdock image + run: bash scripts/build-crossdock.sh env: DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} diff --git a/.github/workflows/ci-docker-build.yml b/.github/workflows/ci-docker-build.yml index b873a9756d5..3ecb6e2d503 100644 --- a/.github/workflows/ci-docker-build.yml +++ b/.github/workflows/ci-docker-build.yml @@ -13,7 +13,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -21,13 +26,13 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - - uses: actions/setup-node@v3 + - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 with: - node-version: '10' + node-version: '16' - name: Export BRANCH variable uses: ./.github/actions/setup-branch @@ -35,7 +40,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 - name: Build and upload all docker images run: bash scripts/build-upload-docker-images.sh diff --git a/.github/workflows/ci-elasticsearch.yml b/.github/workflows/ci-elasticsearch.yml index 33b54acc3cd..086a86b4f1c 100644 --- a/.github/workflows/ci-elasticsearch.yml +++ b/.github/workflows/ci-elasticsearch.yml @@ -7,6 +7,9 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: elasticsearch: runs-on: ubuntu-latest @@ -24,7 +27,12 @@ jobs: distribution: elasticsearch name: ${{ matrix.version.distribution }} ${{ matrix.version.major }} steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -32,14 +40,14 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 - name: Run elasticsearch integration tests run: bash scripts/es-integration-test.sh ${{ matrix.version.distribution }} ${{ matrix.version.image }} diff --git a/.github/workflows/ci-grpc-badger.yml b/.github/workflows/ci-grpc-badger.yml index b18b1be4686..4874fcf0409 100644 --- a/.github/workflows/ci-grpc-badger.yml +++ b/.github/workflows/ci-grpc-badger.yml @@ -7,15 +7,23 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: grpc-and-badger: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Run Badger storage integration tests run: make badger-storage-integration-test diff --git a/.github/workflows/ci-hotrod.yml b/.github/workflows/ci-hotrod.yml index 506b7f754c7..9dd12b6f500 100644 --- a/.github/workflows/ci-hotrod.yml +++ b/.github/workflows/ci-hotrod.yml @@ -11,7 +11,12 @@ jobs: hotrod: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -19,9 +24,9 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Export BRANCH variable uses: ./.github/actions/setup-branch @@ -29,7 +34,7 @@ jobs: - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 - name: Build, test, and publish hotrod image run: bash scripts/hotrod-integration-test.sh diff --git a/.github/workflows/ci-kafka.yml b/.github/workflows/ci-kafka.yml index 48a110185d6..90527cf07cb 100644 --- a/.github/workflows/ci-kafka.yml +++ b/.github/workflows/ci-kafka.yml @@ -7,15 +7,23 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: kafka: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Run kafka integration tests run: bash scripts/kafka-integration-test.sh diff --git a/.github/workflows/ci-opensearch.yml b/.github/workflows/ci-opensearch.yml index f96e76cc276..f9d43b9708d 100644 --- a/.github/workflows/ci-opensearch.yml +++ b/.github/workflows/ci-opensearch.yml @@ -7,6 +7,9 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: opensearch: runs-on: ubuntu-latest @@ -16,9 +19,17 @@ jobs: - major: 1.x image: 1.0.0 distribution: opensearch + - major: 2.x + image: 2.3.0 + distribution: opensearch name: ${{ matrix.version.distribution }} ${{ matrix.version.major }} steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -26,14 +37,14 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Install tools run: make install-ci - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 - name: Run opensearch integration tests run: bash scripts/es-integration-test.sh ${{ matrix.version.distribution }} ${{ matrix.version.image }} diff --git a/.github/workflows/ci-protogen-tests.yml b/.github/workflows/ci-protogen-tests.yml index 1cf0dadf373..5823787d8be 100644 --- a/.github/workflows/ci-protogen-tests.yml +++ b/.github/workflows/ci-protogen-tests.yml @@ -7,17 +7,25 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: protogen: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Run protogen validation run: make proto && git diff --name-status --exit-code diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 1865d2bbac3..13d4acce099 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -10,7 +10,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: submodules: true @@ -18,13 +23,13 @@ jobs: run: | git fetch --prune --unshallow --tags - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - - uses: actions/setup-node@v3 + - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 with: - node-version: '10' + node-version: '16' - name: Export BRANCH variable uses: ./.github/actions/setup-branch @@ -42,15 +47,15 @@ jobs: if: steps.build-binaries.outcome == 'success' - name: Upload binaries - uses: svenstaro/upload-release-action@2.3.0 + uses: svenstaro/upload-release-action@133984371c30d34e38222a64855679a414cb7575 with: - file: deploy/*.tar.gz + file: '{deploy/*.tar.gz,deploy/*.zip,deploy/*.sha256sum.txt}' file_glob: true tag: ${{ github.ref }} repo_token: ${{ secrets.GITHUB_TOKEN }} if: steps.package-binaries.outcome == 'success' - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 - name: Build and upload all docker images run: bash scripts/build-upload-docker-images.sh @@ -69,3 +74,8 @@ jobs: env: DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} + + - name: SBOM Generation + uses: anchore/sbom-action@b7e8507c6a3c89b7099a0198366d862c8f3ad8f1 + with: + artifact-name: jaeger-SBOM.spdx.json diff --git a/.github/workflows/ci-unit-tests.yml b/.github/workflows/ci-unit-tests.yml index ebb03d5e45b..48da2f5a380 100644 --- a/.github/workflows/ci-unit-tests.yml +++ b/.github/workflows/ci-unit-tests.yml @@ -7,18 +7,32 @@ on: pull_request: branches: [main] +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +env: + # Using upload token helps against rate limiting errors. + # Cannot define it as secret as we need it accessible from forks. + # See https://github.com/codecov/codecov-action/issues/837 + CODECOV_TOKEN: f457b710-93af-4191-8678-bcf51281f98c + jobs: unit-tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - - uses: actions/setup-go@v3 - with: - go-version: 1.18.x + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f + with: + go-version: 1.19.x - - name: Install tools - run: make install-ci + - name: Install tools + run: make install-ci - - name: Run unit tests - run: make test-ci + - name: Run unit tests + run: make test-ci diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 20a8f8fce3a..dbd19f83c2b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -10,6 +10,9 @@ on: schedule: - cron: '31 6 * * 1' +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: codeql-analyze: name: CodeQL Analyze @@ -26,12 +29,17 @@ jobs: # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@ec3cf9c605b848da5f1e41e8452719eb1ccfb9a6 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -40,7 +48,7 @@ jobs: # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@ec3cf9c605b848da5f1e41e8452719eb1ccfb9a6 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@ec3cf9c605b848da5f1e41e8452719eb1ccfb9a6 diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 5fa0fc753fe..0c5c5d0b76f 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -12,11 +12,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@2e205a28d0e1da00c5f53b161f4067b052c61f34 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f with: - go-version: 1.18.x + go-version: 1.19.x - name: Add GOPATH run: | @@ -25,7 +30,7 @@ jobs: echo "$GOPATH/bin" >>"$GITHUB_PATH" - name: Run FOSSA scan and upload report - uses: fossa-contrib/fossa-action@v1.2.0 + uses: fossa-contrib/fossa-action@6cffaa064112e1cf9b5798c6224f9487dc1ec316 with: # FOSSA Push-Only API Token fossa-api-key: 304657e2357ba57b416b94e6b119131b diff --git a/.gitignore b/.gitignore index 5d44809d293..0d3e55bbf95 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ cmd/collector/collector cmd/collector/collector-* cmd/ingester/ingester cmd/ingester/ingester-* +cmd/remote-storage/remote-storage +cmd/remote-storage/remote-storage-* cmd/es-index-cleaner/es-index-cleaner-* cmd/es-rollover/es-rollover-* cmd/query/query @@ -39,4 +41,8 @@ crossdock/crossdock-* run-crossdock.log proto-gen/.patched-otel-proto/ __pycache__ - +.asset-manifest.json +deploy/ +deploy-staging/ +sha256sum.combined.txt +.gocache diff --git a/.golangci.yml b/.golangci.yml index fd19b856bad..64c96a7ef39 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,4 +1,5 @@ run: + go: '1.19' timeout: 10m skip-dirs: - mocks @@ -26,7 +27,7 @@ linters-settings: - G404 - G601 gosimple: - go: "1.18" + go: "1.19" linters: enable: @@ -38,6 +39,7 @@ linters: - gosec - govet - misspell + - bidichk disable: - errcheck diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f461663846..b2ef6562fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ next release ------------------- ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes #### New Features @@ -13,11 +13,64 @@ next release ### UI Changes -1.36.0 (2022-07-05) +1.38.1 (2022-10-04) +------------------- +### Backend Changes + +#### Bug fixes, Minor Improvements + +* Bump github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3939](https://github.com/jaegertracing/jaeger/pull/3939)) +* Bump github.com/apache/thrift from 0.16.0 to 0.17.0 ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3936](https://github.com/jaegertracing/jaeger/pull/3936)) +* Bump github.com/hashicorp/go-hclog from 1.2.2 to 1.3.1 ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3934](https://github.com/jaegertracing/jaeger/pull/3934)) +* Bump go.opentelemetry.io/otel from 1.9.0 to 1.10.0 ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3932](https://github.com/jaegertracing/jaeger/pull/3932)) +* Bump github.com/hashicorp/go-plugin from 1.4.4 to 1.4.5 ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3930](https://github.com/jaegertracing/jaeger/pull/3930)) +* Bump github.com/spf13/viper from 1.12.0 to 1.13.0 ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3931](https://github.com/jaegertracing/jaeger/pull/3931)) +* Bump OTEL dependencies => v0.60.0 and grpc => v1.49.0 ([@yurishkuro](https://github.com/yurishkuro) in [#3928](https://github.com/jaegertracing/jaeger/pull/3928)) +* Bump golang.org/x/net to 2022-09-26 ([@yurishkuro](https://github.com/yurishkuro) in [#3927](https://github.com/jaegertracing/jaeger/pull/3927)) +* Bump codecov/codecov-action from 3.1.0 to 3.1.1 ([@dependabot[bot]](https://github.com/apps/dependabot) in [#3917](https://github.com/jaegertracing/jaeger/pull/3917)) + +### UI Changes + +* UI pinned to version [1.27.1](https://github.com/jaegertracing/jaeger-ui/blob/main/CHANGELOG.md#v1271-2022-10-04) to bump dependencies. + + +1.38.0 (2022-09-16) +------------------- +### Backend Changes + +#### Bug fixes, Minor Improvements + +* fix: jaeger-agent sampling endpoint returns backwards incompatible JSON ([@vprithvi](https://github.com/vprithvi) in [#3897](https://github.com/jaegertracing/jaeger/pull/3897)) +* fix: streaming span writer is not working in grpc based remote storage plugin ([@arajkumar](https://github.com/arajkumar) in [#3887](https://github.com/jaegertracing/jaeger/pull/3887)) +* Fix race condition when adding collector tags ([@yurishkuro](https://github.com/yurishkuro) in [#3886](https://github.com/jaegertracing/jaeger/pull/3886)) +* Change build info date to commit timestamp ([@TripleDogDare](https://github.com/TripleDogDare) in [#3876](https://github.com/jaegertracing/jaeger/pull/3876)) +* Add 🚗 ([@yurishkuro](https://github.com/yurishkuro) in [55a8ca9](https://github.com/jaegertracing/jaeger/commit/55a8ca97e3772579b395ffbe4b937a4f5993b008)) +* Add AdditionalDialOptions to ConnBuilder ([@vprithvi](https://github.com/vprithvi) in [#3865](https://github.com/jaegertracing/jaeger/pull/3865)) +* Add sample docker-compose configuration using Kafka ([@yurishkuro](https://github.com/yurishkuro) in [7006e9f](https://github.com/jaegertracing/jaeger/commit/7006e9fe50c8467ad6b84f2072a3cf136bfbe4ec)) + +### UI Changes + +* UI pinned to version [1.27.0 - see the changelog](https://github.com/jaegertracing/jaeger-ui/blob/main/CHANGELOG.md#v1270-2022-09-15). + +1.37.0 (2022-08-03) ------------------- ### Backend Changes -#### Breaking Changes +* Add remote-storage service ([@yurishkuro](https://github.com/yurishkuro) in [#3836](https://github.com/jaegertracing/jaeger/pull/3836)) + +#### Bug fixes, Minor Improvements + +* Fix ingester panic when span.process=nil ([@locmai](https://github.com/locmai) in [#3819](https://github.com/jaegertracing/jaeger/pull/3819)) +* Added windows zip file generation ([@adhithyasrinivasan](https://github.com/adhithyasrinivasan) in [#3817](https://github.com/jaegertracing/jaeger/pull/3817)) +* Refactor gRPC storage plugin for better composability ([@yurishkuro](https://github.com/yurishkuro) in [#3833](https://github.com/jaegertracing/jaeger/pull/3833)) + +### UI Changes + +* UI pinned to version [1.26.0 - see the changelog](https://github.com/jaegertracing/jaeger-ui/blob/main/CHANGELOG.md#v1260-2022-08-03). + +1.36.0 (2022-07-05) +------------------- +### Backend Changes #### New Features * Add flag to enable span size metrics reporting ([@ymtdzzz](https://github.com/ymtdzzz) in [#3782](https://github.com/jaegertracing/jaeger/pull/3782)) @@ -184,7 +237,7 @@ next release ------------------- ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes * Remove deprecated `--badger.truncate` CLI flag ([@yurishkuro](https://github.com/yurishkuro) in [#3410](https://github.com/jaegertracing/jaeger/pull/3410)) @@ -295,7 +348,7 @@ next release ------------------- ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes * Upgrade Badger from v1.6.2 to v3.2103.0 ([#3096](https://github.com/jaegertracing/jaeger/pull/3096), [@Ashmita152](https://github.com/Ashmita152)): * Deprecated `--badger.truncate` flag. @@ -325,7 +378,7 @@ next release #### New Features -#### Breaking Changes +#### ⛔ Breaking Changes * Remove unused `--es-archive.max-span-age` flag ([#2865](https://github.com/jaegertracing/jaeger/pull/2865), [@albertteoh](https://github.com/albertteoh)): @@ -353,7 +406,7 @@ next release ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes * Remove deprecated TLS flags ([#2790](https://github.com/jaegertracing/jaeger/issues/2790), [@albertteoh](https://github.com/albertteoh)): * `--cassandra.tls` is replaced by `--cassandra.tls.enabled` @@ -476,7 +529,7 @@ next release ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes * Configurable ES doc count ([#2453](https://github.com/jaegertracing/jaeger/pull/2453), [@albertteoh](https://github.com/albertteoh)) The `--es.max-num-spans` flag has been deprecated in favour of `--es.max-doc-count`. @@ -601,7 +654,7 @@ The pull request [#2297](https://github.com/jaegertracing/jaeger/pull/2297) aime * CVE-2020-10750: jaegertracing/jaeger: credentials leaked to container logs ([@chlunde](https://github.com/chlunde)) -#### Breaking Changes +#### ⛔ Breaking Changes #### New Features * Add ppc64le support ([#2293](https://github.com/jaegertracing/jaeger/pull/2293), [@Siddhesh-Ghadi](https://github.com/Siddhesh-Ghadi)) @@ -628,7 +681,7 @@ The pull request [#2297](https://github.com/jaegertracing/jaeger/pull/2297) aime ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes * Remove Tchannel between agent and collector ([#2115](https://github.com/jaegertracing/jaeger/pull/2115), [#2112](https://github.com/jaegertracing/jaeger/pull/2112), [@pavolloffay](https://github.com/pavolloffay)) @@ -758,7 +811,7 @@ The pull request [#2297](https://github.com/jaegertracing/jaeger/pull/2297) aime ### Backend Changes -#### Breaking Changes +#### ⛔ Breaking Changes ##### List of service operations can be classified by span kinds ([#1943](https://github.com/jaegertracing/jaeger/pull/1943), [#1942](https://github.com/jaegertracing/jaeger/pull/1942), [#1937](https://github.com/jaegertracing/jaeger/pull/1937), [@guo0693](https://github.com/guo0693)) @@ -879,7 +932,7 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes * The default value for the Ingester's flag `ingester.deadlockInterval` has been changed to `0` ([#1868](https://github.com/jaegertracing/jaeger/pull/1868), [@jpkrohling](https://github.com/jpkrohling)) @@ -917,7 +970,7 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes * Create ES index templates instead of indices ([#1627](https://github.com/jaegertracing/jaeger/pull/1627), [@pavolloffay](https://github.com/pavolloffay)) @@ -976,10 +1029,6 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes - -##### New Features - ##### Bug fixes, Minor Improvements * Change default for bearer-token-propagation to false ([#1642](https://github.com/jaegertracing/jaeger/pull/1642), [@wsoula](https://github.com/wsoula)) @@ -991,7 +1040,7 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes * The traces related metrics on collector now have a new tag `sampler_type` ([#1576](https://github.com/jaegertracing/jaeger/pull/1576), [@guanw](https://github.com/guanw)) @@ -1037,7 +1086,7 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - The `kafka` flags were removed in favor of `kafka.producer` and `kafka.consumer` flags ([#1424](https://github.com/jaegertracing/jaeger/pull/1424), [@ledor473](https://github.com/ledor473)) The following flags have been **removed** in the Collector and the Ingester: @@ -1106,7 +1155,7 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - Introduce `kafka.producer` and `kafka.consumer` flags to replace `kafka` flags ([#1360](https://github.com/jaegertracing/jaeger/pull/1360), [@ledor473](https://github.com/ledor473)) The following flags have been deprecated in the Collector and the Ingester: @@ -1154,10 +1203,6 @@ running curator would immediately remove the old index. - Discover dependencies table version automatically ([#1364](https://github.com/jaegertracing/jaeger/pull/1364), [@black-adder](https://github.com/black-adder)) -##### Breaking Changes - -##### New Features - ##### Bug fixes, Minor Improvements - Separate query-service functionality from http handler ([#1312](https://github.com/jaegertracing/jaeger/pull/1312), [@annanay25](https://github.com/annanay25)) @@ -1170,7 +1215,7 @@ running curator would immediately remove the old index. #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - Remove cassandra SASI indices ([#1328](https://github.com/jaegertracing/jaeger/pull/1328), [@black-adder](https://github.com/black-adder)) @@ -1209,7 +1254,7 @@ Users who wish to continue to use the v1 table don't have to do anything as the #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - Change Elasticsearch index prefix from `:` to `-` ([#1284](https://github.com/jaegertracing/jaeger/pull/1284), [@pavolloffay](https://github.com/pavolloffay)) @@ -1340,7 +1385,7 @@ jaeger_http_server_errors{source="collector-proxy",status="5xx"} #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - Refactor agent configuration ([#1092](https://github.com/jaegertracing/jaeger/pull/1092), [@pavolloffay](https://github.com/pavolloffay)) @@ -1427,7 +1472,7 @@ jaeger_query_responses_bucket{operation="find_traces",le="0.005"} 2 #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - `jaeger-standalone` binary has been renamed to `jaeger-all-in-one`. This change also includes package rename from `standalone` to `all-in-one` ([#1062](https://github.com/jaegertracing/jaeger/pull/1062), [@pavolloffay](https://github.com/pavolloffay)) @@ -1446,7 +1491,7 @@ jaeger_query_responses_bucket{operation="find_traces",le="0.005"} 2 #### Backend Changes -##### Breaking Changes +##### ⛔ Breaking Changes - The storage implementations no longer write the parentSpanID field to storage (#856). If you are upgrading to this version, **you must upgrade query service first**! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b9def300748..832b87f7fa6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -185,10 +185,7 @@ requires connection to Cassandra ``` ## Merging PRs -For maintainers: before merging a PR make sure: -* the title is descriptive and follows [a good commit message](./CONTRIBUTING_GUIDELINES.md) -* pull request is assigned to the current release milestone -* add `changelog:*` and other labels +**For maintainers:** before merging a PR make sure the title is descriptive and follows [a good commit message](./CONTRIBUTING_GUIDELINES.md) Merge the PR by using "Squash and merge" option on Github. Avoid creating merge commits. After the merge make sure referenced issues were closed. diff --git a/Makefile b/Makefile index 1d0cc87a9ec..ae5ab215039 100644 --- a/Makefile +++ b/Makefile @@ -33,8 +33,9 @@ else endif GOOS ?= $(shell go env GOOS) GOARCH ?= $(shell go env GOARCH) -GOBUILD=CGO_ENABLED=0 installsuffix=cgo go build -trimpath -GOTEST=go test -v $(RACE) +GOCACHE=$(abspath .gocache) +GOBUILD=GOCACHE=$(GOCACHE) CGO_ENABLED=0 installsuffix=cgo go build -trimpath +GOTEST=GOCACHE=$(GOCACHE) go test -v $(RACE) GOFMT=gofmt GOFUMPT=gofumpt FMT_LOG=.fmt.log @@ -42,7 +43,7 @@ IMPORT_LOG=.import.log GIT_SHA=$(shell git rev-parse HEAD) GIT_CLOSEST_TAG=$(shell git describe --abbrev=0 --tags) -DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') +DATE=$(shell date -u -d @$(shell git show -s --format=%ct) +'%Y-%m-%dT%H:%M:%SZ') BUILD_INFO_IMPORT_PATH=$(JAEGER_IMPORT_PATH)/pkg/version BUILD_INFO=-ldflags "-X $(BUILD_INFO_IMPORT_PATH).commitSHA=$(GIT_SHA) -X $(BUILD_INFO_IMPORT_PATH).latestVersion=$(GIT_CLOSEST_TAG) -X $(BUILD_INFO_IMPORT_PATH).date=$(DATE)" @@ -82,6 +83,9 @@ go-gen: clean: rm -rf cover.out .cover/ cover.html $(FMT_LOG) $(IMPORT_LOG) \ jaeger-ui/packages/jaeger-ui/build + find ./cmd/query/app/ui/actual -type f -name '*.gz' -delete + GOCACHE=$(GOCACHE) go clean -cache -testcache + find cmd -type f -executable | xargs -I{} sh -c '(git ls-files --error-unmatch {} 2>/dev/null || rm -v {})' .PHONY: test test: go-gen @@ -170,15 +174,15 @@ build-tracegen: .PHONY: build-anonymizer build-anonymizer: - $(GOBUILD) -o ./cmd/anonymizer/anonymizer-$(GOOS)-$(GOARCH) ./cmd/anonymizer/main.go + $(GOBUILD) -o ./cmd/anonymizer/anonymizer-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/anonymizer/main.go .PHONY: build-esmapping-generator build-esmapping-generator: - $(GOBUILD) -o ./plugin/storage/es/esmapping-generator-$(GOOS)-$(GOARCH) ./cmd/esmapping-generator/main.go + $(GOBUILD) -o ./plugin/storage/es/esmapping-generator-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/esmapping-generator/main.go .PHONY: build-esmapping-generator-linux build-esmapping-generator-linux: - GOOS=linux GOARCH=amd64 $(GOBUILD) -o ./plugin/storage/es/esmapping-generator ./cmd/esmapping-generator/main.go + GOOS=linux GOARCH=amd64 $(GOBUILD) -o ./plugin/storage/es/esmapping-generator $(BUILD_INFO) ./cmd/esmapping-generator/main.go .PHONY: build-es-index-cleaner build-es-index-cleaner: @@ -200,10 +204,10 @@ run-all-in-one: build-ui build-ui: cmd/query/app/ui/actual/index.html.gz cmd/query/app/ui/actual/index.html.gz: jaeger-ui/packages/jaeger-ui/build/index.html - rm -rf cmd/query/app/ui/actual - mkdir cmd/query/app/ui/actual + # do not delete dot-files + rm -rf cmd/query/app/ui/actual/* cp -r jaeger-ui/packages/jaeger-ui/build/* cmd/query/app/ui/actual/ - find cmd/query/app/ui/actual -type f | xargs gzip + find cmd/query/app/ui/actual -type f | grep -v .gitignore | xargs gzip --no-name jaeger-ui/packages/jaeger-ui/build/index.html: $(MAKE) rebuild-ui @@ -216,8 +220,8 @@ rebuild-ui: build-all-in-one-linux: GOOS=linux $(MAKE) build-all-in-one -build-all-in-one-debug build-agent-debug build-query-debug build-collector-debug build-ingester-debug: DISABLE_OPTIMIZATIONS = -gcflags="all=-N -l" -build-all-in-one-debug build-agent-debug build-query-debug build-collector-debug build-ingester-debug: SUFFIX = -debug +build-all-in-one-debug build-agent-debug build-query-debug build-collector-debug build-ingester-debug build-remote-storage-debug: DISABLE_OPTIMIZATIONS = -gcflags="all=-N -l" +build-all-in-one-debug build-agent-debug build-query-debug build-collector-debug build-ingester-debug build-remote-storage-debug: SUFFIX = -debug .PHONY: build-all-in-one build-all-in-one-debug build-all-in-one build-all-in-one-debug: build-ui @@ -239,6 +243,10 @@ build-collector build-collector-debug: build-ingester build-ingester-debug: $(GOBUILD) $(DISABLE_OPTIMIZATIONS) -o ./cmd/ingester/ingester$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/ingester/main.go +.PHONY: build-remote-storage build-remote-storage-debug +build-remote-storage build-remote-storage-debug: + $(GOBUILD) $(DISABLE_OPTIMIZATIONS) -o ./cmd/remote-storage/remote-storage$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/remote-storage/main.go + .PHONY: build-binaries-linux build-binaries-linux: GOOS=linux GOARCH=amd64 $(MAKE) build-platform-binaries @@ -276,7 +284,10 @@ build-platform-binaries: build-agent \ build-query-debug \ build-ingester \ build-ingester-debug \ + build-remote-storage \ + build-remote-storage-debug \ build-all-in-one \ + build-all-in-one-debug \ build-examples \ build-tracegen \ build-anonymizer \ @@ -381,8 +392,8 @@ draft-release: .PHONY: install-tools install-tools: - go install github.com/vektra/mockery/v2@v2.10.4 - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2 + go install github.com/vektra/mockery/v2@v2.14.0 + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0 go install mvdan.cc/gofumpt@latest .PHONY: install-ci @@ -591,3 +602,14 @@ certs: .PHONY: certs-dryrun certs-dryrun: cd pkg/config/tlscfg/testdata && ./gen-certs.sh -d + +.PHONY: repro-check +repro-check: + # Check local reproducibility of generated executables. + $(MAKE) clean + $(MAKE) build-all-platforms + # Generate checksum for all executables under ./cmd + find cmd -type f -executable -exec shasum -b -a 256 {} \; | sort -k2 | tee sha256sum.combined.txt + $(MAKE) clean + $(MAKE) build-all-platforms + shasum -b -a 256 --strict --check ./sha256sum.combined.txt diff --git a/README.md b/README.md index 3e594e53de6..7ff60460731 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ [Upstream project README](https://github.com/jaegertracing/jaeger) ## Pre-reqs + 1. Install go - `brew install golang`. 1. Install yarn - `brew install yarn` (for Jaeger UI). ## How to Build + 1. `make build-ui`. 1. `make build-all-in-one`. ## How to Run + 1. `make run-all-in-one`. +<<<<<<< HEAD + ## How to Debug + 1. in VScode, go to Run and Debug (cmd+shift+D) and run using of the configured run options. diff --git a/RELEASE.md b/RELEASE.md index ee7536c0436..032e711b64c 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -13,21 +13,20 @@ ``` * Even if a submodule does not have a new release, it should be checked to see if there were any changes warranting cutting a new release and then including it. * Rotate the below release managers table placing yourself at the bottom. The date should be the first Wednesday of the month. -2. Add all merged pull requests to the milestone for the release and create a new milestone for a next release e.g. `Release 1.16`. -3. After the PR is merged, create a release on Github: +2. After the PR is merged, create a release on Github: * Automated: * `make draft-release` * Manual: * Title "Release X.Y.Z" * Tag `vX.Y.Z` (note the `v` prefix) and choose appropriate branch * Copy the new CHANGELOG.md section into the release notes -5. The release tag will trigger a build of the docker images. Since forks don't have jaegertracingbot dockerhub token, they can never publish images to jaegertracing organisation. +3. The release tag will trigger a build of the docker images. Since forks don't have jaegertracingbot dockerhub token, they can never publish images to jaegertracing organisation. 1. Check the images are available on [Docker Hub](https://hub.docker.com/r/jaegertracing/). 2. For monitoring and troubleshooting, refer to the [jaegertracing/jaeger GithubActions tab](https://github.com/jaegertracing/jaeger/actions). -6. [Publish documentation](https://github.com/jaegertracing/documentation/blob/main/RELEASE.md) for the new version in [jaegertracing.io](https://www.jaegertracing.io/docs/latest). +4. [Publish documentation](https://github.com/jaegertracing/documentation/blob/main/RELEASE.md) for the new version in [jaegertracing.io](https://www.jaegertracing.io/docs/latest). 1. Check [jaegertracing.io](https://www.jaegertracing.io/docs/latest) redirects to the new documentation release version URL. 2. For monitoring and troubleshooting, refer to the [jaegertracing/documentation GithubActions tab](https://github.com/jaegertracing/documentation/actions). -7. Announce the release on the [mailing list](https://groups.google.com/g/jaeger-tracing), [slack](https://cloud-native.slack.com/archives/CGG7NFUJ3), and [twitter](https://twitter.com/JaegerTracing?lang=en). +5. Announce the release on the [mailing list](https://groups.google.com/g/jaeger-tracing), [slack](https://cloud-native.slack.com/archives/CGG7NFUJ3), and [twitter](https://twitter.com/JaegerTracing?lang=en). Maintenance branches should follow naming convention: `release-major.minor` (e.g.`release-1.8`). @@ -53,7 +52,7 @@ Here are the release managers for future versions with the tentative release dat | Version | Release Manager | Tentative release date | |---------|-----------------|------------------------| -| 1.37.0 | @joe-elliott | 3 August 2022 | -| 1.38.0 | @pavolloffay | 7 September 2022 | -| 1.39.0 | @yurishkuro | 2 October 2022 | -| 1.40.0 | @albertteoh | 2 November 2022 | +| 1.39.0 | @albertteoh | 2 November 2022 | +| 1.40.0 | @joe-elliott | 7 December 2022 | +| 1.41.0 | @pavolloffay | 4 January 2023 | +| 1.42.0 | @yurishkuro | 1 February 2023 | diff --git a/cmd/agent/app/httpserver/srv.go b/cmd/agent/app/httpserver/srv.go index 7d4bbaaebcc..698bad33a7e 100644 --- a/cmd/agent/app/httpserver/srv.go +++ b/cmd/agent/app/httpserver/srv.go @@ -17,6 +17,7 @@ package httpserver import ( "net/http" + "time" "github.com/gorilla/mux" "go.uber.org/zap" @@ -39,8 +40,9 @@ func NewHTTPServer(hostPort string, manager configmanager.ClientConfigManager, m handler.RegisterRoutes(r) errorLog, _ := zap.NewStdLogAt(logger, zapcore.ErrorLevel) return &http.Server{ - Addr: hostPort, - Handler: r, - ErrorLog: errorLog, + Addr: hostPort, + Handler: r, + ErrorLog: errorLog, + ReadHeaderTimeout: 2 * time.Second, } } diff --git a/cmd/agent/app/reporter/client_metrics.go b/cmd/agent/app/reporter/client_metrics.go index 1bfd8d82101..63c90028dd2 100644 --- a/cmd/agent/app/reporter/client_metrics.go +++ b/cmd/agent/app/reporter/client_metrics.go @@ -123,7 +123,7 @@ func (r *ClientMetricsReporter) EmitBatch(ctx context.Context, batch *jaeger.Bat // Close stops background gc goroutine for client stats map. func (r *ClientMetricsReporter) Close() error { - if r.closed.CAS(false, true) { + if r.closed.CompareAndSwap(false, true) { close(r.shutdown) } return nil diff --git a/cmd/agent/app/reporter/grpc/builder.go b/cmd/agent/app/reporter/grpc/builder.go index ccc2a071c22..9e4bb2c932c 100644 --- a/cmd/agent/app/reporter/grpc/builder.go +++ b/cmd/agent/app/reporter/grpc/builder.go @@ -47,6 +47,8 @@ type ConnBuilder struct { DiscoveryMinPeers int Notifier discovery.Notifier Discoverer discovery.Discoverer + + AdditionalDialOptions []grpc.DialOption } // NewConnBuilder creates a new grpc connection builder. @@ -96,6 +98,8 @@ func (b *ConnBuilder) CreateConnection(logger *zap.Logger, mFactory metrics.Fact } dialOptions = append(dialOptions, grpc.WithDefaultServiceConfig(grpcresolver.GRPCServiceConfig)) dialOptions = append(dialOptions, grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(grpc_retry.WithMax(b.MaxRetry)))) + dialOptions = append(dialOptions, b.AdditionalDialOptions...) + conn, err := grpc.Dial(dialTarget, dialOptions...) if err != nil { return nil, err diff --git a/cmd/agent/app/reporter/grpc/builder_test.go b/cmd/agent/app/reporter/grpc/builder_test.go index 0f792171149..f70de066cf4 100644 --- a/cmd/agent/app/reporter/grpc/builder_test.go +++ b/cmd/agent/app/reporter/grpc/builder_test.go @@ -395,3 +395,33 @@ func assertConnectionState(t *testing.T, conn *grpc.ClientConn, expectedState st } } } + +type fakeInterceptor struct { + isCalled bool +} + +func (f *fakeInterceptor) intercept(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + f.isCalled = true + return invoker(ctx, method, req, reply, cc, opts...) +} + +func (f *fakeInterceptor) assertCalled(t *testing.T) { + assert.True(t, f.isCalled) +} + +func TestBuilderWithAdditionalDialOptions(t *testing.T) { + fi := fakeInterceptor{} + defer fi.assertCalled(t) + + cb := ConnBuilder{ + CollectorHostPorts: []string{"127.0.0.1:14268"}, + AdditionalDialOptions: []grpc.DialOption{grpc.WithUnaryInterceptor(fi.intercept)}, + } + + r, err := cb.CreateConnection(zap.NewNop(), metrics.NullFactory) + require.NoError(t, err) + assert.NotNil(t, r) + + err = r.Invoke(context.Background(), "test", map[string]string{}, map[string]string{}, []grpc.CallOption{}...) + assert.Error(t, err, "should error because no server is running") +} diff --git a/cmd/agent/app/servers/thriftudp/transport.go b/cmd/agent/app/servers/thriftudp/transport.go index 6bb1a1af7bf..4579502061f 100644 --- a/cmd/agent/app/servers/thriftudp/transport.go +++ b/cmd/agent/app/servers/thriftudp/transport.go @@ -46,7 +46,8 @@ var _ thrift.TTransport = (*TUDPTransport)(nil) // All writes are buffered and flushed in one UDP packet. If locHostPort is not "", it // will be used as the local address for the connection // Example: -// trans, err := thriftudp.NewTUDPClientTransport("192.168.1.1:9090", "") +// +// trans, err := thriftudp.NewTUDPClientTransport("192.168.1.1:9090", "") func NewTUDPClientTransport(destHostPort string, locHostPort string) (*TUDPTransport, error) { destAddr, err := net.ResolveUDPAddr("udp", destHostPort) if err != nil { @@ -75,7 +76,8 @@ func createClient(destAddr, locAddr *net.UDPAddr) (*TUDPTransport, error) { // NewTUDPServerTransport creates a net.UDPConn-backed TTransport for Thrift servers // It will listen for incoming udp packets on the specified host/port // Example: -// trans, err := thriftudp.NewTUDPClientTransport("localhost:9001") +// +// trans, err := thriftudp.NewTUDPClientTransport("localhost:9001") func NewTUDPServerTransport(hostPort string) (*TUDPTransport, error) { addr, err := net.ResolveUDPAddr("udp", hostPort) if err != nil { diff --git a/cmd/all-in-one/main.go b/cmd/all-in-one/main.go index c3ceb111d77..963b224d5c5 100644 --- a/cmd/all-in-one/main.go +++ b/cmd/all-in-one/main.go @@ -156,7 +156,7 @@ by default uses only in-memory database.`, logger.Fatal("Failed to configure query service", zap.Error(err)) } - tm := tenancy.NewTenancyManager(&cOpts.GRPC.Tenancy) + tm := tenancy.NewManager(&cOpts.GRPC.Tenancy) // collector c := collectorApp.New(&collectorApp.CollectorParams{ @@ -269,7 +269,7 @@ func startQuery( depReader dependencystore.Reader, metricsQueryService querysvc.MetricsQueryService, baseFactory metrics.Factory, - tm *tenancy.TenancyManager, + tm *tenancy.Manager, ) *queryApp.Server { spanReader = storageMetrics.NewReadMetricsDecorator(spanReader, baseFactory.Namespace(metrics.NSOptions{Name: "query"})) qs := querysvc.NewQueryService(spanReader, depReader, *queryOpts) diff --git a/cmd/collector/app/collector.go b/cmd/collector/app/collector.go index 2beff366f01..1d45f9bb58e 100644 --- a/cmd/collector/app/collector.go +++ b/cmd/collector/app/collector.go @@ -53,7 +53,7 @@ type Collector struct { hCheck *healthcheck.HealthCheck spanProcessor processor.SpanProcessor spanHandlers *SpanHandlers - tenancyMgr *tenancy.TenancyManager + tenancyMgr *tenancy.Manager // state, read only hServer *http.Server @@ -74,7 +74,7 @@ type CollectorParams struct { StrategyStore strategystore.StrategyStore Aggregator strategystore.Aggregator HealthCheck *healthcheck.HealthCheck - TenancyMgr *tenancy.TenancyManager + TenancyMgr *tenancy.Manager } // New constructs a new collector component, ready to be started diff --git a/cmd/collector/app/collector_test.go b/cmd/collector/app/collector_test.go index cf951f22e23..a3036cd42ab 100644 --- a/cmd/collector/app/collector_test.go +++ b/cmd/collector/app/collector_test.go @@ -54,7 +54,7 @@ func TestNewCollector(t *testing.T) { baseMetrics := metricstest.NewFactory(time.Hour) spanWriter := &fakeSpanWriter{} strategyStore := &mockStrategyStore{} - tm := &tenancy.TenancyManager{} + tm := &tenancy.Manager{} c := New(&CollectorParams{ ServiceName: "collector", @@ -80,7 +80,7 @@ func TestCollector_StartErrors(t *testing.T) { baseMetrics := metricstest.NewFactory(time.Hour) spanWriter := &fakeSpanWriter{} strategyStore := &mockStrategyStore{} - tm := &tenancy.TenancyManager{} + tm := &tenancy.Manager{} c := New(&CollectorParams{ ServiceName: "collector", @@ -135,7 +135,7 @@ func TestCollector_PublishOpts(t *testing.T) { metricsFactory := fork.New("internal", forkFactory, baseMetrics) spanWriter := &fakeSpanWriter{} strategyStore := &mockStrategyStore{} - tm := &tenancy.TenancyManager{} + tm := &tenancy.Manager{} c := New(&CollectorParams{ ServiceName: "collector", @@ -171,7 +171,7 @@ func TestAggregator(t *testing.T) { spanWriter := &fakeSpanWriter{} strategyStore := &mockStrategyStore{} agg := &mockAggregator{} - tm := &tenancy.TenancyManager{} + tm := &tenancy.Manager{} c := New(&CollectorParams{ ServiceName: "collector", diff --git a/cmd/collector/app/flags/flags.go b/cmd/collector/app/flags/flags.go index bc614348fbe..e9a2daf3d2c 100644 --- a/cmd/collector/app/flags/flags.go +++ b/cmd/collector/app/flags/flags.go @@ -228,11 +228,7 @@ func (opts *GRPCOptions) initFromViper(v *viper.Viper, logger *zap.Logger, cfg s } else { return fmt.Errorf("failed to parse gRPC TLS options: %w", err) } - if tenancy, err := tenancy.InitFromViper(v); err == nil { - opts.Tenancy = tenancy - } else { - return fmt.Errorf("failed to parse Tenancy options: %w", err) - } + opts.Tenancy = tenancy.InitFromViper(v) return nil } diff --git a/cmd/collector/app/flags/flags_test.go b/cmd/collector/app/flags/flags_test.go index 194f600dc79..527f7ea7a52 100644 --- a/cmd/collector/app/flags/flags_test.go +++ b/cmd/collector/app/flags/flags_test.go @@ -120,7 +120,7 @@ func TestCollectorOptionsWithFlags_CheckSimpleTenancy(t *testing.T) { c := &CollectorOptions{} v, command := config.Viperize(AddFlags) command.ParseFlags([]string{ - "--multi_tenancy.enabled=true", + "--multi-tenancy.enabled=true", }) c.InitFromViper(v, zap.NewNop()) @@ -132,9 +132,9 @@ func TestCollectorOptionsWithFlags_CheckFullTenancy(t *testing.T) { c := &CollectorOptions{} v, command := config.Viperize(AddFlags) command.ParseFlags([]string{ - "--multi_tenancy.enabled=true", - "--multi_tenancy.header=custom-tenant-header", - "--multi_tenancy.tenants=acme,hardware-store", + "--multi-tenancy.enabled=true", + "--multi-tenancy.header=custom-tenant-header", + "--multi-tenancy.tenants=acme,hardware-store", }) c.InitFromViper(v, zap.NewNop()) diff --git a/cmd/collector/app/handler/grpc_handler.go b/cmd/collector/app/handler/grpc_handler.go index 1c6d9e79f08..9e23ec602cf 100644 --- a/cmd/collector/app/handler/grpc_handler.go +++ b/cmd/collector/app/handler/grpc_handler.go @@ -36,7 +36,7 @@ type GRPCHandler struct { } // NewGRPCHandler registers routes for this handler on the given router. -func NewGRPCHandler(logger *zap.Logger, spanProcessor processor.SpanProcessor, tenancyMgr *tenancy.TenancyManager) *GRPCHandler { +func NewGRPCHandler(logger *zap.Logger, spanProcessor processor.SpanProcessor, tenancyMgr *tenancy.Manager) *GRPCHandler { return &GRPCHandler{ logger: logger, batchConsumer: newBatchConsumer(logger, @@ -58,10 +58,10 @@ type batchConsumer struct { logger *zap.Logger spanProcessor processor.SpanProcessor spanOptions processor.SpansOptions - tenancyMgr *tenancy.TenancyManager + tenancyMgr *tenancy.Manager } -func newBatchConsumer(logger *zap.Logger, spanProcessor processor.SpanProcessor, transport processor.InboundTransport, spanFormat processor.SpanFormat, tenancyMgr *tenancy.TenancyManager) batchConsumer { +func newBatchConsumer(logger *zap.Logger, spanProcessor processor.SpanProcessor, transport processor.InboundTransport, spanFormat processor.SpanFormat, tenancyMgr *tenancy.Manager) batchConsumer { return batchConsumer{ logger: logger, spanProcessor: spanProcessor, diff --git a/cmd/collector/app/handler/grpc_handler_test.go b/cmd/collector/app/handler/grpc_handler_test.go index 171835e578c..17a8af07e52 100644 --- a/cmd/collector/app/handler/grpc_handler_test.go +++ b/cmd/collector/app/handler/grpc_handler_test.go @@ -98,7 +98,7 @@ func newClient(t *testing.T, addr net.Addr) (api_v2.CollectorServiceClient, *grp func TestPostSpans(t *testing.T) { processor := &mockSpanProcessor{} server, addr := initializeGRPCTestServer(t, func(s *grpc.Server) { - handler := NewGRPCHandler(zap.NewNop(), processor, &tenancy.TenancyManager{}) + handler := NewGRPCHandler(zap.NewNop(), processor, &tenancy.Manager{}) api_v2.RegisterCollectorServiceServer(s, handler) }) defer server.Stop() @@ -133,7 +133,7 @@ func TestPostSpans(t *testing.T) { func TestGRPCCompressionEnabled(t *testing.T) { processor := &mockSpanProcessor{} server, addr := initializeGRPCTestServer(t, func(s *grpc.Server) { - handler := NewGRPCHandler(zap.NewNop(), processor, &tenancy.TenancyManager{}) + handler := NewGRPCHandler(zap.NewNop(), processor, &tenancy.Manager{}) api_v2.RegisterCollectorServiceServer(s, handler) }) defer server.Stop() @@ -171,7 +171,7 @@ func TestPostSpansWithError(t *testing.T) { processor := &mockSpanProcessor{expectedError: test.processorError} logger, logBuf := testutils.NewLogger() server, addr := initializeGRPCTestServer(t, func(s *grpc.Server) { - handler := NewGRPCHandler(logger, processor, &tenancy.TenancyManager{}) + handler := NewGRPCHandler(logger, processor, &tenancy.Manager{}) api_v2.RegisterCollectorServiceServer(s, handler) }) defer server.Stop() @@ -210,7 +210,7 @@ func TestPostTenantedSpans(t *testing.T) { processor := &mockSpanProcessor{} server, addr := initializeGRPCTestServer(t, func(s *grpc.Server) { handler := NewGRPCHandler(zap.NewNop(), processor, - tenancy.NewTenancyManager(&tenancy.Options{ + tenancy.NewManager(&tenancy.Options{ Enabled: true, Header: tenantHeader, Tenants: []string{dummyTenant}, @@ -346,7 +346,7 @@ func TestGetTenant(t *testing.T) { processor := &mockSpanProcessor{} handler := NewGRPCHandler(zap.NewNop(), processor, - tenancy.NewTenancyManager(&tenancy.Options{ + tenancy.NewManager(&tenancy.Options{ Enabled: true, Header: tenantHeader, Tenants: validTenants, diff --git a/cmd/collector/app/handler/otlp_receiver.go b/cmd/collector/app/handler/otlp_receiver.go index 678fdd78b6d..40efd31154d 100644 --- a/cmd/collector/app/handler/otlp_receiver.go +++ b/cmd/collector/app/handler/otlp_receiver.go @@ -28,6 +28,7 @@ import ( "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/receiver/otlpreceiver" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/collector/app/flags" @@ -40,7 +41,7 @@ import ( var _ component.Host = (*otelHost)(nil) // API check // StartOTLPReceiver starts OpenTelemetry OTLP receiver listening on gRPC and HTTP ports. -func StartOTLPReceiver(options *flags.CollectorOptions, logger *zap.Logger, spanProcessor processor.SpanProcessor, tm *tenancy.TenancyManager) (component.TracesReceiver, error) { +func StartOTLPReceiver(options *flags.CollectorOptions, logger *zap.Logger, spanProcessor processor.SpanProcessor, tm *tenancy.Manager) (component.TracesReceiver, error) { otlpFactory := otlpreceiver.NewFactory() return startOTLPReceiver( options, @@ -60,7 +61,7 @@ func startOTLPReceiver( options *flags.CollectorOptions, logger *zap.Logger, spanProcessor processor.SpanProcessor, - tm *tenancy.TenancyManager, + tm *tenancy.Manager, // from here: params that can be mocked in tests otlpFactory component.ReceiverFactory, newTraces func(consume consumer.ConsumeTracesFunc, options ...consumer.Option) (consumer.Traces, error), @@ -73,7 +74,8 @@ func startOTLPReceiver( otlpReceiverSettings := component.ReceiverCreateSettings{ TelemetrySettings: component.TelemetrySettings{ Logger: logger, - TracerProvider: otel.GetTracerProvider(), // TODO we may always want no-op here, not the global default + TracerProvider: otel.GetTracerProvider(), // TODO we may always want no-op here, not the global default + MeterProvider: metric.NewNoopMeterProvider(), // TODO wire this with jaegerlib metrics? }, } @@ -140,7 +142,7 @@ func applyTLSSettings(opts *tlscfg.Options) *configtls.TLSServerSetting { } } -func newConsumerDelegate(logger *zap.Logger, spanProcessor processor.SpanProcessor, tm *tenancy.TenancyManager) *consumerDelegate { +func newConsumerDelegate(logger *zap.Logger, spanProcessor processor.SpanProcessor, tm *tenancy.Manager) *consumerDelegate { return &consumerDelegate{ batchConsumer: newBatchConsumer(logger, spanProcessor, diff --git a/cmd/collector/app/handler/otlp_receiver_test.go b/cmd/collector/app/handler/otlp_receiver_test.go index b05b5ef5d0b..4f7d9490fa2 100644 --- a/cmd/collector/app/handler/otlp_receiver_test.go +++ b/cmd/collector/app/handler/otlp_receiver_test.go @@ -49,7 +49,7 @@ func optionsWithPorts(port string) *flags.CollectorOptions { func TestStartOtlpReceiver(t *testing.T) { spanProcessor := &mockSpanProcessor{} logger, _ := testutils.NewLogger() - tm := &tenancy.TenancyManager{} + tm := &tenancy.Manager{} rec, err := StartOTLPReceiver(optionsWithPorts(":0"), logger, spanProcessor, tm) require.NoError(t, err) defer func() { @@ -81,7 +81,7 @@ func TestConsumerDelegate(t *testing.T) { t.Run(test.expectLog, func(t *testing.T) { logger, logBuf := testutils.NewLogger() spanProcessor := &mockSpanProcessor{expectedError: test.expectErr} - consumer := newConsumerDelegate(logger, spanProcessor, &tenancy.TenancyManager{}) + consumer := newConsumerDelegate(logger, spanProcessor, &tenancy.Manager{}) err := consumer.consume(context.Background(), makeTracesOneSpan()) @@ -100,7 +100,7 @@ func TestStartOtlpReceiver_Error(t *testing.T) { spanProcessor := &mockSpanProcessor{} logger, _ := testutils.NewLogger() opts := optionsWithPorts(":-1") - tm := &tenancy.TenancyManager{} + tm := &tenancy.Manager{} _, err := StartOTLPReceiver(opts, logger, spanProcessor, tm) require.Error(t, err) assert.Contains(t, err.Error(), "could not start the OTLP receiver") @@ -109,7 +109,7 @@ func TestStartOtlpReceiver_Error(t *testing.T) { return nil, errors.New("mock error") } f := otlpreceiver.NewFactory() - _, err = startOTLPReceiver(opts, logger, spanProcessor, &tenancy.TenancyManager{}, f, newTraces, f.CreateTracesReceiver) + _, err = startOTLPReceiver(opts, logger, spanProcessor, &tenancy.Manager{}, f, newTraces, f.CreateTracesReceiver) require.Error(t, err) assert.Contains(t, err.Error(), "could not create the OTLP consumer") @@ -118,7 +118,7 @@ func TestStartOtlpReceiver_Error(t *testing.T) { ) (component.TracesReceiver, error) { return nil, errors.New("mock error") } - _, err = startOTLPReceiver(opts, logger, spanProcessor, &tenancy.TenancyManager{}, f, consumer.NewTraces, createTracesReceiver) + _, err = startOTLPReceiver(opts, logger, spanProcessor, &tenancy.Manager{}, f, consumer.NewTraces, createTracesReceiver) require.Error(t, err) assert.Contains(t, err.Error(), "could not create the OTLP receiver") } diff --git a/cmd/collector/app/sampling/gprc_handler.go b/cmd/collector/app/sampling/grpc_handler.go similarity index 100% rename from cmd/collector/app/sampling/gprc_handler.go rename to cmd/collector/app/sampling/grpc_handler.go diff --git a/cmd/collector/app/sampling/strategystore/factory.go b/cmd/collector/app/sampling/strategystore/factory.go index e47bd3105b0..5fab77b62bd 100644 --- a/cmd/collector/app/sampling/strategystore/factory.go +++ b/cmd/collector/app/sampling/strategystore/factory.go @@ -24,7 +24,7 @@ import ( // Factory defines an interface for a factory that can create implementations of different strategy storage components. // Implementations are also encouraged to implement plugin.Configurable interface. // -// See also +// # See also // // plugin.Configurable type Factory interface { diff --git a/cmd/collector/app/server/grpc_test.go b/cmd/collector/app/server/grpc_test.go index bdf722a8206..9acc872b5c4 100644 --- a/cmd/collector/app/server/grpc_test.go +++ b/cmd/collector/app/server/grpc_test.go @@ -40,7 +40,7 @@ func TestFailToListen(t *testing.T) { logger, _ := zap.NewDevelopment() server, err := StartGRPCServer(&GRPCServerParams{ HostPort: ":-1", - Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.TenancyManager{}), + Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.Manager{}), SamplingStore: &mockSamplingStore{}, Logger: logger, }) @@ -57,7 +57,7 @@ func TestFailServe(t *testing.T) { logger := zap.New(core) serveGRPC(grpc.NewServer(), lis, &GRPCServerParams{ - Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.TenancyManager{}), + Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.Manager{}), SamplingStore: &mockSamplingStore{}, Logger: logger, OnError: func(e error) { @@ -72,7 +72,7 @@ func TestFailServe(t *testing.T) { func TestSpanCollector(t *testing.T) { logger, _ := zap.NewDevelopment() params := &GRPCServerParams{ - Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.TenancyManager{}), + Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.Manager{}), SamplingStore: &mockSamplingStore{}, Logger: logger, MaxReceiveMessageLength: 1024 * 1024, @@ -97,7 +97,7 @@ func TestSpanCollector(t *testing.T) { func TestCollectorStartWithTLS(t *testing.T) { logger, _ := zap.NewDevelopment() params := &GRPCServerParams{ - Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.TenancyManager{}), + Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.Manager{}), SamplingStore: &mockSamplingStore{}, Logger: logger, TLSConfig: tlscfg.Options{ @@ -116,7 +116,7 @@ func TestCollectorStartWithTLS(t *testing.T) { func TestCollectorReflection(t *testing.T) { logger, _ := zap.NewDevelopment() params := &GRPCServerParams{ - Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.TenancyManager{}), + Handler: handler.NewGRPCHandler(logger, &mockSpanProcessor{}, &tenancy.Manager{}), SamplingStore: &mockSamplingStore{}, Logger: logger, } diff --git a/cmd/collector/app/server/http.go b/cmd/collector/app/server/http.go index ab5e67e17ed..97024412e5b 100644 --- a/cmd/collector/app/server/http.go +++ b/cmd/collector/app/server/http.go @@ -17,6 +17,7 @@ package server import ( "net" "net/http" + "time" "github.com/gorilla/mux" "go.uber.org/zap" @@ -49,8 +50,9 @@ func StartHTTPServer(params *HTTPServerParams) (*http.Server, error) { errorLog, _ := zap.NewStdLogAt(params.Logger, zapcore.ErrorLevel) server := &http.Server{ - Addr: params.HostPort, - ErrorLog: errorLog, + Addr: params.HostPort, + ErrorLog: errorLog, + ReadHeaderTimeout: 2 * time.Second, } if params.TLSConfig.Enabled { tlsCfg, err := params.TLSConfig.Config(params.Logger) // This checks if the certificates are correctly provided diff --git a/cmd/collector/app/server/zipkin.go b/cmd/collector/app/server/zipkin.go index c7f7fcefb3e..a4bf36d999d 100644 --- a/cmd/collector/app/server/zipkin.go +++ b/cmd/collector/app/server/zipkin.go @@ -18,6 +18,7 @@ import ( "net" "net/http" "strings" + "time" "github.com/gorilla/mux" "github.com/rs/cors" @@ -61,8 +62,9 @@ func StartZipkinServer(params *ZipkinServerParams) (*http.Server, error) { errorLog, _ := zap.NewStdLogAt(params.Logger, zapcore.ErrorLevel) server := &http.Server{ - Addr: params.HostPort, - ErrorLog: errorLog, + Addr: params.HostPort, + ErrorLog: errorLog, + ReadHeaderTimeout: 2 * time.Second, } if params.TLSConfig.Enabled { tlsCfg, err := params.TLSConfig.Config(params.Logger) // This checks if the certificates are correctly provided diff --git a/cmd/collector/app/span_handler_builder.go b/cmd/collector/app/span_handler_builder.go index dd5dd311702..6f87ab97327 100644 --- a/cmd/collector/app/span_handler_builder.go +++ b/cmd/collector/app/span_handler_builder.go @@ -36,7 +36,7 @@ type SpanHandlerBuilder struct { CollectorOpts *flags.CollectorOptions Logger *zap.Logger MetricsFactory metrics.Factory - TenancyMgr *tenancy.TenancyManager + TenancyMgr *tenancy.Manager } // SpanHandlers holds instances to the span handlers built by the SpanHandlerBuilder diff --git a/cmd/collector/app/span_handler_builder_test.go b/cmd/collector/app/span_handler_builder_test.go index 3f747f83372..9799a2ad25a 100644 --- a/cmd/collector/app/span_handler_builder_test.go +++ b/cmd/collector/app/span_handler_builder_test.go @@ -42,7 +42,7 @@ func TestNewSpanHandlerBuilder(t *testing.T) { builder := &SpanHandlerBuilder{ SpanWriter: spanWriter, CollectorOpts: cOpts, - TenancyMgr: &tenancy.TenancyManager{}, + TenancyMgr: &tenancy.Manager{}, } assert.NotNil(t, builder.logger()) assert.NotNil(t, builder.metricsFactory()) @@ -52,7 +52,7 @@ func TestNewSpanHandlerBuilder(t *testing.T) { CollectorOpts: cOpts, Logger: zap.NewNop(), MetricsFactory: metrics.NullFactory, - TenancyMgr: &tenancy.TenancyManager{}, + TenancyMgr: &tenancy.Manager{}, } spanProcessor := builder.BuildSpanProcessor() diff --git a/cmd/collector/app/span_processor.go b/cmd/collector/app/span_processor.go index 5e9ff4acafd..140acbd3325 100644 --- a/cmd/collector/app/span_processor.go +++ b/cmd/collector/app/span_processor.go @@ -177,6 +177,16 @@ func (sp *spanProcessor) ProcessSpans(mSpans []*model.Span, options processor.Sp sp.preProcessSpans(mSpans, options.Tenant) sp.metrics.BatchSize.Update(int64(len(mSpans))) retMe := make([]bool, len(mSpans)) + + // Note: this is not the ideal place to do this because collector tags are added to Process.Tags, + // and Process can be shared between different spans in the batch, but we no longer know that, + // the relation is lost upstream and it's impossible in Go to dedupe pointers. But at least here + // we have a single thread updating all spans that may share the same Process, before concurrency + // kicks in. + for _, span := range mSpans { + sp.addCollectorTags(span) + } + for i, mSpan := range mSpans { ok := sp.enqueueSpan(mSpan, options.SpanFormat, options.InboundTransport, options.Tenant) if !ok && sp.reportBusy { @@ -213,6 +223,8 @@ func (sp *spanProcessor) addCollectorTags(span *model.Span) { typedTags.Sort() } +// Note: spans may share the Process object, so no changes should be made to Process +// in this function as it may cause race conditions. func (sp *spanProcessor) enqueueSpan(span *model.Span, originalFormat processor.SpanFormat, transport processor.InboundTransport, tenant string) bool { spanCounts := sp.metrics.GetCountsForFormat(originalFormat, transport) spanCounts.ReceivedBySvc.ReportServiceNameForSpan(span) @@ -225,9 +237,6 @@ func (sp *spanProcessor) enqueueSpan(span *model.Span, originalFormat processor. // add format tag span.Tags = append(span.Tags, model.String("internal.span.format", string(originalFormat))) - // append the collector tags - sp.addCollectorTags(span) - item := &queueItem{ queuedTime: time.Now(), span: span, diff --git a/cmd/collector/main.go b/cmd/collector/main.go index f92c9482689..290b02401b2 100644 --- a/cmd/collector/main.go +++ b/cmd/collector/main.go @@ -103,7 +103,7 @@ func main() { if err != nil { logger.Fatal("Failed to initialize collector", zap.Error(err)) } - tm := tenancy.NewTenancyManager(&collectorOpts.GRPC.Tenancy) + tm := tenancy.NewManager(&collectorOpts.GRPC.Tenancy) collector := app.New(&app.CollectorParams{ ServiceName: serviceName, diff --git a/cmd/flags/admin.go b/cmd/flags/admin.go index 31dd979993f..e3e7a069fa3 100644 --- a/cmd/flags/admin.go +++ b/cmd/flags/admin.go @@ -23,6 +23,7 @@ import ( "net" "net/http" "net/http/pprof" + "time" "github.com/spf13/viper" "go.uber.org/zap" @@ -132,8 +133,9 @@ func (s *AdminServer) serveWithListener(l net.Listener) { recoveryHandler := recoveryhandler.NewRecoveryHandler(s.logger, true) errorLog, _ := zap.NewStdLogAt(s.logger, zapcore.ErrorLevel) s.server = &http.Server{ - Handler: recoveryHandler(s.mux), - ErrorLog: errorLog, + Handler: recoveryHandler(s.mux), + ErrorLog: errorLog, + ReadHeaderTimeout: 2 * time.Second, } if s.tlsCfg != nil { s.server.TLSConfig = s.tlsCfg diff --git a/cmd/ingester/app/consumer/deadlock_detector.go b/cmd/ingester/app/consumer/deadlock_detector.go index bac160f7d00..7e8885a08f6 100644 --- a/cmd/ingester/app/consumer/deadlock_detector.go +++ b/cmd/ingester/app/consumer/deadlock_detector.go @@ -36,7 +36,6 @@ import ( // the dead instance. // // This hack protects jaeger-ingester from issues described in https://github.com/jaegertracing/jaeger/issues/1052 -// type deadlockDetector struct { metricsFactory metrics.Factory logger *zap.Logger diff --git a/cmd/ingester/app/processor/span_processor.go b/cmd/ingester/app/processor/span_processor.go index ad1c8aa7479..4817103384b 100644 --- a/cmd/ingester/app/processor/span_processor.go +++ b/cmd/ingester/app/processor/span_processor.go @@ -19,6 +19,7 @@ import ( "fmt" "io" + "github.com/jaegertracing/jaeger/cmd/collector/app/sanitizer" "github.com/jaegertracing/jaeger/plugin/storage/kafka" "github.com/jaegertracing/jaeger/storage/spanstore" ) @@ -45,6 +46,7 @@ type SpanProcessorParams struct { // KafkaSpanProcessor implements SpanProcessor for Kafka messages type KafkaSpanProcessor struct { unmarshaller kafka.Unmarshaller + sanitizer sanitizer.SanitizeSpan writer spanstore.Writer io.Closer } @@ -54,6 +56,7 @@ func NewSpanProcessor(params SpanProcessorParams) *KafkaSpanProcessor { return &KafkaSpanProcessor{ unmarshaller: params.Unmarshaller, writer: params.Writer, + sanitizer: sanitizer.NewChainedSanitizer(sanitizer.NewStandardSanitizers()...), } } @@ -63,6 +66,7 @@ func (s KafkaSpanProcessor) Process(message Message) error { if err != nil { return fmt.Errorf("cannot unmarshall byte array into span: %w", err) } + // TODO context should be propagated from upstream components - return s.writer.WriteSpan(context.TODO(), span) + return s.writer.WriteSpan(context.TODO(), s.sanitizer(span)) } diff --git a/cmd/ingester/app/processor/span_processor_test.go b/cmd/ingester/app/processor/span_processor_test.go index 0fae62e278f..5df0159b7a6 100644 --- a/cmd/ingester/app/processor/span_processor_test.go +++ b/cmd/ingester/app/processor/span_processor_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" cmocks "github.com/jaegertracing/jaeger/cmd/ingester/app/consumer/mocks" "github.com/jaegertracing/jaeger/model" @@ -33,25 +34,32 @@ func TestNewSpanProcessor(t *testing.T) { } func TestSpanProcessor_Process(t *testing.T) { - writer := &smocks.Writer{} - unmarshallerMock := &umocks.Unmarshaller{} - processor := &KafkaSpanProcessor{ - unmarshaller: unmarshallerMock, - writer: writer, - } + mockUnmarshaller := &umocks.Unmarshaller{} + mockWriter := &smocks.Writer{} + processor := NewSpanProcessor(SpanProcessorParams{ + Unmarshaller: mockUnmarshaller, + Writer: mockWriter, + }) message := &cmocks.Message{} - data := []byte("police") - span := &model.Span{} + data := []byte("irrelevant, mock unmarshaller should return the span") + span := &model.Span{ + Process: nil, // we want to make sure sanitizers will fix this data issue. + } message.On("Value").Return(data) - unmarshallerMock.On("Unmarshal", data).Return(span, nil) - writer.On("WriteSpan", context.Background(), span).Return(nil) + mockUnmarshaller.On("Unmarshal", data).Return(span, nil) + mockWriter.On("WriteSpan", context.Background(), span). + Return(nil). + Run(func(args mock.Arguments) { + span := args[1].(*model.Span) + assert.NotNil(t, span.Process, "sanitizer must fix Process=nil data issue") + }) assert.Nil(t, processor.Process(message)) message.AssertExpectations(t) - writer.AssertExpectations(t) + mockWriter.AssertExpectations(t) } func TestSpanProcessor_ProcessError(t *testing.T) { diff --git a/cmd/query/app/apiv3/grpc_gateway.go b/cmd/query/app/apiv3/grpc_gateway.go index f563f7158e9..0abc723bbc5 100644 --- a/cmd/query/app/apiv3/grpc_gateway.go +++ b/cmd/query/app/apiv3/grpc_gateway.go @@ -31,7 +31,7 @@ import ( ) // RegisterGRPCGateway registers api_v3 endpoints into provided mux. -func RegisterGRPCGateway(ctx context.Context, logger *zap.Logger, r *mux.Router, basePath string, grpcEndpoint string, grpcTLS tlscfg.Options, tm *tenancy.TenancyManager) error { +func RegisterGRPCGateway(ctx context.Context, logger *zap.Logger, r *mux.Router, basePath string, grpcEndpoint string, grpcTLS tlscfg.Options, tm *tenancy.Manager) error { jsonpb := &runtime.JSONPb{} muxOpts := []runtime.ServeMuxOption{ diff --git a/cmd/query/app/apiv3/grpc_gateway_test.go b/cmd/query/app/apiv3/grpc_gateway_test.go index 92b9ac5a4cf..b950b371408 100644 --- a/cmd/query/app/apiv3/grpc_gateway_test.go +++ b/cmd/query/app/apiv3/grpc_gateway_test.go @@ -66,7 +66,7 @@ func setupGRPCGateway(t *testing.T, basePath string, serverTLS tlscfg.Options, c serverGRPCOpts = append(serverGRPCOpts, grpc.Creds(creds)) } if tenancyOptions.Enabled { - tm := tenancy.NewTenancyManager(&tenancyOptions) + tm := tenancy.NewManager(&tenancyOptions) serverGRPCOpts = append(serverGRPCOpts, grpc.StreamInterceptor(tenancy.NewGuardingStreamInterceptor(tm)), grpc.UnaryInterceptor(tenancy.NewGuardingUnaryInterceptor(tm)), @@ -86,7 +86,7 @@ func setupGRPCGateway(t *testing.T, basePath string, serverTLS tlscfg.Options, c router := &mux.Router{} router = router.PathPrefix(basePath).Subrouter() ctx, cancel := context.WithCancel(context.Background()) - err := RegisterGRPCGateway(ctx, zap.NewNop(), router, basePath, lis.Addr().String(), clientTLS, tenancy.NewTenancyManager(&tenancyOptions)) + err := RegisterGRPCGateway(ctx, zap.NewNop(), router, basePath, lis.Addr().String(), clientTLS, tenancy.NewManager(&tenancyOptions)) require.NoError(t, err) httpLis, err := net.Listen("tcp", ":0") @@ -176,7 +176,7 @@ func TestTenancyGRPCGateway(t *testing.T) { tenancyOptions := tenancy.Options{ Enabled: true, } - tm := tenancy.NewTenancyManager(&tenancyOptions) + tm := tenancy.NewManager(&tenancyOptions) testGRPCGatewayWithTenancy(t, "/", tlscfg.Options{}, tlscfg.Options{}, // Configure the gateway to forward tenancy header from HTTP to GRPC tenancyOptions, @@ -217,7 +217,7 @@ func TestTenancyGRPCRejection(t *testing.T) { require.Equal(t, http.StatusForbidden, response.StatusCode) // Try again with tenant header set - tm := tenancy.NewTenancyManager(&tenancyOptions) + tm := tenancy.NewManager(&tenancyOptions) req.Header.Set(tm.Header, "acme") response, err = http.DefaultClient.Do(req) require.NoError(t, err) diff --git a/cmd/query/app/flags.go b/cmd/query/app/flags.go index 07dd9392a5d..7a729fd8ec1 100644 --- a/cmd/query/app/flags.go +++ b/cmd/query/app/flags.go @@ -125,11 +125,7 @@ func (qOpts *QueryOptions) InitFromViper(v *viper.Viper, logger *zap.Logger) (*Q } else { qOpts.AdditionalHeaders = headers } - if tenancy, err := tenancy.InitFromViper(v); err == nil { - qOpts.Tenancy = tenancy - } else { - return qOpts, fmt.Errorf("failed to parse Tenancy options: %w", err) - } + qOpts.Tenancy = tenancy.InitFromViper(v) return qOpts, nil } @@ -146,7 +142,7 @@ func (qOpts *QueryOptions) BuildQueryServiceOptions(storageFactory storage.Facto } // stringSliceAsHeader parses a slice of strings and returns a http.Header. -// Each string in the slice is expected to be in the format "key: value" +// Each string in the slice is expected to be in the format "key: value" func stringSliceAsHeader(slice []string) (http.Header, error) { if len(slice) == 0 { return nil, nil diff --git a/cmd/query/app/grpc_handler_test.go b/cmd/query/app/grpc_handler_test.go index c25b890ccf3..8384bafb40b 100644 --- a/cmd/query/app/grpc_handler_test.go +++ b/cmd/query/app/grpc_handler_test.go @@ -145,7 +145,7 @@ type grpcClient struct { conn *grpc.ClientConn } -func newGRPCServer(t *testing.T, q *querysvc.QueryService, mq querysvc.MetricsQueryService, logger *zap.Logger, tracer opentracing.Tracer, tenancyMgr *tenancy.TenancyManager) (*grpc.Server, net.Addr) { +func newGRPCServer(t *testing.T, q *querysvc.QueryService, mq querysvc.MetricsQueryService, logger *zap.Logger, tracer opentracing.Tracer, tenancyMgr *tenancy.Manager) (*grpc.Server, net.Addr) { lis, _ := net.Listen("tcp", ":0") var grpcOpts []grpc.ServerOption if tenancyMgr.Enabled { @@ -203,7 +203,7 @@ func withMetricsQuery() testOption { } func withServerAndClient(t *testing.T, actualTest func(server *grpcServer, client *grpcClient), options ...testOption) { - server := initializeTenantedTestServerGRPCWithOptions(t, &tenancy.TenancyManager{}, options...) + server := initializeTenantedTestServerGRPCWithOptions(t, &tenancy.Manager{}, options...) client := newGRPCClient(t, server.lisAddr.String()) defer server.server.Stop() defer client.conn.Close() @@ -901,7 +901,7 @@ func TestMetricsQueryNilRequestGRPC(t *testing.T) { assert.EqualError(t, err, errNilRequest.Error()) } -func initializeTenantedTestServerGRPCWithOptions(t *testing.T, tm *tenancy.TenancyManager, options ...testOption) *grpcServer { +func initializeTenantedTestServerGRPCWithOptions(t *testing.T, tm *tenancy.Manager, options ...testOption) *grpcServer { archiveSpanReader := &spanstoremocks.Reader{} archiveSpanWriter := &spanstoremocks.Writer{} @@ -942,7 +942,7 @@ func initializeTenantedTestServerGRPCWithOptions(t *testing.T, tm *tenancy.Tenan } } -func withTenantedServerAndClient(t *testing.T, tm *tenancy.TenancyManager, actualTest func(server *grpcServer, client *grpcClient), options ...testOption) { +func withTenantedServerAndClient(t *testing.T, tm *tenancy.Manager, actualTest func(server *grpcServer, client *grpcClient), options ...testOption) { server := initializeTenantedTestServerGRPCWithOptions(t, tm, options...) client := newGRPCClient(t, server.lisAddr.String()) defer server.server.Stop() @@ -960,7 +960,7 @@ func withOutgoingMetadata(t *testing.T, ctx context.Context, headerName, headerV } func TestSearchTenancyGRPC(t *testing.T) { - tm := tenancy.NewTenancyManager(&tenancy.Options{ + tm := tenancy.NewManager(&tenancy.Options{ Enabled: true, }) withTenantedServerAndClient(t, tm, func(server *grpcServer, client *grpcClient) { @@ -996,7 +996,7 @@ func TestSearchTenancyGRPC(t *testing.T) { } func TestServicesTenancyGRPC(t *testing.T) { - tm := tenancy.NewTenancyManager(&tenancy.Options{ + tm := tenancy.NewManager(&tenancy.Options{ Enabled: true, }) withTenantedServerAndClient(t, tm, func(server *grpcServer, client *grpcClient) { @@ -1015,7 +1015,7 @@ func TestServicesTenancyGRPC(t *testing.T) { } func TestSearchTenancyGRPCExplicitList(t *testing.T) { - tm := tenancy.NewTenancyManager(&tenancy.Options{ + tm := tenancy.NewManager(&tenancy.Options{ Enabled: true, Header: "non-standard-tenant-header", Tenants: []string{"mercury", "venus", "mars"}, @@ -1097,7 +1097,7 @@ func TestSearchTenancyGRPCExplicitList(t *testing.T) { } func TestTenancyContextFlowGRPC(t *testing.T) { - tm := tenancy.NewTenancyManager(&tenancy.Options{ + tm := tenancy.NewManager(&tenancy.Options{ Enabled: true, }) withTenantedServerAndClient(t, tm, func(server *grpcServer, client *grpcClient) { diff --git a/cmd/query/app/http_handler.go b/cmd/query/app/http_handler.go index 8abde1b9b74..49b7805a628 100644 --- a/cmd/query/app/http_handler.go +++ b/cmd/query/app/http_handler.go @@ -85,7 +85,7 @@ type APIHandler struct { queryService *querysvc.QueryService metricsQueryService querysvc.MetricsQueryService queryParser queryParser - tenancyMgr *tenancy.TenancyManager + tenancyMgr *tenancy.Manager basePath string apiPrefix string logger *zap.Logger @@ -93,7 +93,7 @@ type APIHandler struct { } // NewAPIHandler returns an APIHandler -func NewAPIHandler(queryService *querysvc.QueryService, tm *tenancy.TenancyManager, options ...HandlerOption) *APIHandler { +func NewAPIHandler(queryService *querysvc.QueryService, tm *tenancy.Manager, options ...HandlerOption) *APIHandler { aH := &APIHandler{ queryService: queryService, queryParser: queryParser{ diff --git a/cmd/query/app/http_handler_test.go b/cmd/query/app/http_handler_test.go index 881b164cf4d..ae6124feffd 100644 --- a/cmd/query/app/http_handler_test.go +++ b/cmd/query/app/http_handler_test.go @@ -93,7 +93,7 @@ type structuredTraceResponse struct { func initializeTestServerWithHandler(queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) *testServer { return initializeTestServerWithOptions( - &tenancy.TenancyManager{}, + &tenancy.Manager{}, queryOptions, append( []HandlerOption{ @@ -108,7 +108,7 @@ func initializeTestServerWithHandler(queryOptions querysvc.QueryServiceOptions, ) } -func initializeTestServerWithOptions(tenancyMgr *tenancy.TenancyManager, queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) *testServer { +func initializeTestServerWithOptions(tenancyMgr *tenancy.Manager, queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) *testServer { readStorage := &spanstoremocks.Reader{} dependencyStorage := &depsmocks.Reader{} qs := querysvc.NewQueryService(readStorage, dependencyStorage, queryOptions) @@ -135,7 +135,7 @@ type testServer struct { } func withTestServer(doTest func(s *testServer), queryOptions querysvc.QueryServiceOptions, options ...HandlerOption) { - ts := initializeTestServerWithOptions(&tenancy.TenancyManager{}, queryOptions, options...) + ts := initializeTestServerWithOptions(&tenancy.Manager{}, queryOptions, options...) defer ts.server.Close() doTest(ts) } @@ -183,7 +183,7 @@ func TestLogOnServerError(t *testing.T) { apiHandlerOptions := []HandlerOption{ HandlerOptions.Logger(zap.New(l)), } - h := NewAPIHandler(qs, &tenancy.TenancyManager{}, apiHandlerOptions...) + h := NewAPIHandler(qs, &tenancy.Manager{}, apiHandlerOptions...) e := errors.New("test error") h.handleError(&testHttp.TestResponseWriter{}, e, http.StatusInternalServerError) require.Equal(t, 1, len(*l.logs)) @@ -404,7 +404,7 @@ func TestSearchByTraceIDSuccess(t *testing.T) { func TestSearchByTraceIDSuccessWithArchive(t *testing.T) { archiveReadMock := &spanstoremocks.Reader{} - ts := initializeTestServerWithOptions(&tenancy.TenancyManager{}, querysvc.QueryServiceOptions{ + ts := initializeTestServerWithOptions(&tenancy.Manager{}, querysvc.QueryServiceOptions{ ArchiveSpanReader: archiveReadMock, }) defer ts.server.Close() @@ -447,7 +447,7 @@ func TestSearchByTraceIDFailure(t *testing.T) { func TestSearchModelConversionFailure(t *testing.T) { ts := initializeTestServerWithOptions( - &tenancy.TenancyManager{}, + &tenancy.Manager{}, querysvc.QueryServiceOptions{ Adjuster: adjuster.Func(func(trace *model.Trace) (*model.Trace, error) { return trace, errAdjustment @@ -886,7 +886,7 @@ func TestSearchTenancyHTTP(t *testing.T) { Enabled: true, } ts := initializeTestServerWithOptions( - tenancy.NewTenancyManager(&tenancyOptions), + tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) defer ts.server.Close() ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). @@ -913,7 +913,7 @@ func TestSearchTenancyRejectionHTTP(t *testing.T) { Enabled: true, } ts := initializeTestServerWithOptions( - tenancy.NewTenancyManager(&tenancyOptions), + tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) defer ts.server.Close() ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). @@ -927,7 +927,7 @@ func TestSearchTenancyRejectionHTTP(t *testing.T) { assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) - tm := tenancy.NewTenancyManager(&tenancyOptions) + tm := tenancy.NewManager(&tenancyOptions) req.Header.Set(tm.Header, "acme") resp, err = http.DefaultClient.Do(req) require.NoError(t, err) @@ -940,7 +940,7 @@ func TestSearchTenancyFlowTenantHTTP(t *testing.T) { Enabled: true, } ts := initializeTestServerWithOptions( - tenancy.NewTenancyManager(&tenancyOptions), + tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) defer ts.server.Close() ts.spanReader.On("GetTrace", mock.MatchedBy(func(v interface{}) bool { diff --git a/cmd/query/app/query_parser.go b/cmd/query/app/query_parser.go index fae8928a080..9e885f9a9e3 100644 --- a/cmd/query/app/query_parser.go +++ b/cmd/query/app/query_parser.go @@ -101,30 +101,31 @@ func newDurationUnitsParser(units time.Duration) durationParser { // parseTraceQueryParams takes a request and constructs a model of parameters. // // Why start/end parameters are expressed in microseconds: -// Span searches operate on span latencies, which are expressed as microseconds in the data model, hence why -// support for high accuracy in search query parameters is required. -// Microsecond precision is a legacy artifact from zipkin origins where timestamps and durations -// are in microseconds (see: https://zipkin.io/pages/instrumenting.html). +// Span searches operate on span latencies, which are expressed as microseconds in the data model, hence why +// support for high accuracy in search query parameters is required. +// Microsecond precision is a legacy artifact from zipkin origins where timestamps and durations +// are in microseconds (see: https://zipkin.io/pages/instrumenting.html). // // Why duration parameters are expressed as duration strings like "1ms": -// The search UI itself does not insist on exact units because it supports string like 1ms. -// Go makes parsing duration strings like "1ms" very easy, hence why parsing of such strings is -// deferred to the backend rather than Jaeger UI. +// The search UI itself does not insist on exact units because it supports string like 1ms. +// Go makes parsing duration strings like "1ms" very easy, hence why parsing of such strings is +// deferred to the backend rather than Jaeger UI. // // Trace query syntax: -// query ::= param | param '&' query -// param ::= service | operation | limit | start | end | minDuration | maxDuration | tag | tags -// service ::= 'service=' strValue -// operation ::= 'operation=' strValue -// limit ::= 'limit=' intValue -// start ::= 'start=' intValue in unix microseconds -// end ::= 'end=' intValue in unix microseconds -// minDuration ::= 'minDuration=' strValue (units are "ns", "us" (or "µs"), "ms", "s", "m", "h") -// maxDuration ::= 'maxDuration=' strValue (units are "ns", "us" (or "µs"), "ms", "s", "m", "h") -// tag ::= 'tag=' key | 'tag=' keyvalue -// key := strValue -// keyValue := strValue ':' strValue -// tags :== 'tags=' jsonMap +// +// query ::= param | param '&' query +// param ::= service | operation | limit | start | end | minDuration | maxDuration | tag | tags +// service ::= 'service=' strValue +// operation ::= 'operation=' strValue +// limit ::= 'limit=' intValue +// start ::= 'start=' intValue in unix microseconds +// end ::= 'end=' intValue in unix microseconds +// minDuration ::= 'minDuration=' strValue (units are "ns", "us" (or "µs"), "ms", "s", "m", "h") +// maxDuration ::= 'maxDuration=' strValue (units are "ns", "us" (or "µs"), "ms", "s", "m", "h") +// tag ::= 'tag=' key | 'tag=' keyvalue +// key := strValue +// keyValue := strValue ':' strValue +// tags :== 'tags=' jsonMap func (p *queryParser) parseTraceQueryParams(r *http.Request) (*traceQueryParameters, error) { service := r.FormValue(serviceParam) operation := r.FormValue(operationParam) @@ -211,38 +212,39 @@ func (p *queryParser) parseDependenciesQueryParams(r *http.Request) (dqp depende // parseMetricsQueryParams takes a request and constructs a model of metrics query parameters. // // Why the API is designed using an end time (endTs) and lookback: -// The typical usage of the metrics APIs is to view the most recent metrics from now looking -// back a certain period of time, given the value of metrics generally degrades with time. As such, the API -// is also designed to mirror the user interface inputs. +// The typical usage of the metrics APIs is to view the most recent metrics from now looking +// back a certain period of time, given the value of metrics generally degrades with time. As such, the API +// is also designed to mirror the user interface inputs. // // Why times are expressed as unix milliseconds: -// - The minimum step size for Prometheus-compliant metrics backends is 1ms, -// hence millisecond precision on times is sufficient. -// - The metrics API is designed with one primary client in mind, the Jaeger UI. As it is a React.js application, -// the maximum supported built-in time precision is milliseconds, making it a convenient precision to use for such a client. +// - The minimum step size for Prometheus-compliant metrics backends is 1ms, +// hence millisecond precision on times is sufficient. +// - The metrics API is designed with one primary client in mind, the Jaeger UI. As it is a React.js application, +// the maximum supported built-in time precision is milliseconds, making it a convenient precision to use for such a client. // // Why durations are expressed as unix milliseconds: -// - Given the endTs time is expressed as milliseconds, it follows that lookback durations should use the -// same time units to compute the start time. -// - As above, the minimum step size for Prometheus-compliant metrics backends is 1ms. -// - Other durations are in milliseconds to maintain consistency of units with other parameters in the metrics APIs. -// - As the primary client for the metrics API is the Jaeger UI, it is programmatically simpler to supply the -// integer representations of durations in milliseconds rather than the human-readable representation such as "1ms". +// - Given the endTs time is expressed as milliseconds, it follows that lookback durations should use the +// same time units to compute the start time. +// - As above, the minimum step size for Prometheus-compliant metrics backends is 1ms. +// - Other durations are in milliseconds to maintain consistency of units with other parameters in the metrics APIs. +// - As the primary client for the metrics API is the Jaeger UI, it is programmatically simpler to supply the +// integer representations of durations in milliseconds rather than the human-readable representation such as "1ms". // // Metrics query syntax: -// query ::= services , [ '&' optionalParams ] -// optionalParams := param | param '&' optionalParams -// param ::= groupByOperation | endTs | lookback | step | ratePer | spanKinds -// services ::= service | service '&' services -// service ::= 'service=' strValue -// groupByOperation ::= 'groupByOperation=' boolValue -// endTs ::= 'endTs=' intValue in unix milliseconds -// lookback ::= 'lookback=' intValue duration in milliseconds -// step ::= 'step=' intValue duration in milliseconds -// ratePer ::= 'ratePer=' intValue duration in milliseconds -// spanKinds ::= spanKind | spanKind '&' spanKinds -// spanKind ::= 'spanKind=' spanKindType -// spanKindType ::= "unspecified" | "internal" | "server" | "client" | "producer" | "consumer" +// +// query ::= services , [ '&' optionalParams ] +// optionalParams := param | param '&' optionalParams +// param ::= groupByOperation | endTs | lookback | step | ratePer | spanKinds +// services ::= service | service '&' services +// service ::= 'service=' strValue +// groupByOperation ::= 'groupByOperation=' boolValue +// endTs ::= 'endTs=' intValue in unix milliseconds +// lookback ::= 'lookback=' intValue duration in milliseconds +// step ::= 'step=' intValue duration in milliseconds +// ratePer ::= 'ratePer=' intValue duration in milliseconds +// spanKinds ::= spanKind | spanKind '&' spanKinds +// spanKind ::= 'spanKind=' spanKindType +// spanKindType ::= "unspecified" | "internal" | "server" | "client" | "producer" | "consumer" func (p *queryParser) parseMetricsQueryParams(r *http.Request) (bqp metricsstore.BaseQueryParameters, err error) { query := r.URL.Query() services, ok := query[serviceParam] diff --git a/cmd/query/app/server.go b/cmd/query/app/server.go index 721a0491ead..70e2988e7e4 100644 --- a/cmd/query/app/server.go +++ b/cmd/query/app/server.go @@ -65,7 +65,7 @@ type Server struct { } // NewServer creates and initializes Server -func NewServer(logger *zap.Logger, querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.TenancyManager, tracer opentracing.Tracer) (*Server, error) { +func NewServer(logger *zap.Logger, querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.Manager, tracer opentracing.Tracer) (*Server, error) { _, httpPort, err := net.SplitHostPort(options.HTTPHostPort) if err != nil { return nil, err @@ -107,7 +107,7 @@ func (s Server) HealthCheckStatus() chan healthcheck.Status { return s.unavailableChannel } -func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.TenancyManager, logger *zap.Logger, tracer opentracing.Tracer) (*grpc.Server, error) { +func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, options *QueryOptions, tm *tenancy.Manager, logger *zap.Logger, tracer opentracing.Tracer) (*grpc.Server, error) { var grpcOpts []grpc.ServerOption if options.TLSGRPC.Enabled { @@ -151,7 +151,7 @@ func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc. return server, nil } -func createHTTPServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, queryOpts *QueryOptions, tm *tenancy.TenancyManager, tracer opentracing.Tracer, logger *zap.Logger) (*http.Server, context.CancelFunc, error) { +func createHTTPServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc.MetricsQueryService, queryOpts *QueryOptions, tm *tenancy.Manager, tracer opentracing.Tracer, logger *zap.Logger) (*http.Server, context.CancelFunc, error) { apiHandlerOptions := []HandlerOption{ HandlerOptions.Logger(logger), HandlerOptions.Tracer(tracer), @@ -185,8 +185,9 @@ func createHTTPServer(querySvc *querysvc.QueryService, metricsQuerySvc querysvc. errorLog, _ := zap.NewStdLogAt(logger, zapcore.ErrorLevel) server := &http.Server{ - Handler: recoveryHandler(handler), - ErrorLog: errorLog, + Handler: recoveryHandler(handler), + ErrorLog: errorLog, + ReadHeaderTimeout: 2 * time.Second, } if queryOpts.TLSHTTP.Enabled { diff --git a/cmd/query/app/server_test.go b/cmd/query/app/server_test.go index f72373d3434..d1d473d57af 100644 --- a/cmd/query/app/server_test.go +++ b/cmd/query/app/server_test.go @@ -69,7 +69,7 @@ func TestCreateTLSServerSinglePortError(t *testing.T) { _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8080", TLSGRPC: tlsCfg, TLSHTTP: tlsCfg}, - tenancy.NewTenancyManager(&tenancy.Options{}), opentracing.NoopTracer{}) + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.NotNil(t, err) } @@ -83,7 +83,7 @@ func TestCreateTLSGrpcServerError(t *testing.T) { _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8081", TLSGRPC: tlsCfg}, - tenancy.NewTenancyManager(&tenancy.Options{}), opentracing.NoopTracer{}) + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.NotNil(t, err) } @@ -97,7 +97,7 @@ func TestCreateTLSHttpServerError(t *testing.T) { _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8081", TLSHTTP: tlsCfg}, - tenancy.NewTenancyManager(&tenancy.Options{}), opentracing.NoopTracer{}) + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.NotNil(t, err) } @@ -339,7 +339,7 @@ func TestServerHTTPTLS(t *testing.T) { querySvc := querysvc.NewQueryService(spanReader, dependencyReader, querysvc.QueryServiceOptions{}) server, err := NewServer(flagsSvc.Logger, querySvc, nil, - serverOptions, tenancy.NewTenancyManager(&tenancy.Options{}), + serverOptions, tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.Nil(t, err) assert.NoError(t, server.Start()) @@ -499,7 +499,7 @@ func TestServerGRPCTLS(t *testing.T) { querySvc := querysvc.NewQueryService(spanReader, dependencyReader, querysvc.QueryServiceOptions{}) server, err := NewServer(flagsSvc.Logger, querySvc, nil, - serverOptions, tenancy.NewTenancyManager(&tenancy.Options{}), + serverOptions, tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.Nil(t, err) assert.NoError(t, server.Start()) @@ -554,13 +554,13 @@ func TestServerGRPCTLS(t *testing.T) { func TestServerBadHostPort(t *testing.T) { _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: "8080", GRPCHostPort: "127.0.0.1:8081", BearerTokenPropagation: true}, - tenancy.NewTenancyManager(&tenancy.Options{}), + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.NotNil(t, err) _, err = NewServer(zap.NewNop(), &querysvc.QueryService{}, nil, &QueryOptions{HTTPHostPort: "127.0.0.1:8081", GRPCHostPort: "9123", BearerTokenPropagation: true}, - tenancy.NewTenancyManager(&tenancy.Options{}), + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.NotNil(t, err) @@ -591,7 +591,7 @@ func TestServerInUseHostPort(t *testing.T) { GRPCHostPort: tc.grpcHostPort, BearerTokenPropagation: true, }, - tenancy.NewTenancyManager(&tenancy.Options{}), + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}, ) assert.NoError(t, err) @@ -621,7 +621,7 @@ func TestServerSinglePort(t *testing.T) { querySvc := querysvc.NewQueryService(spanReader, dependencyReader, querysvc.QueryServiceOptions{}) server, err := NewServer(flagsSvc.Logger, querySvc, nil, &QueryOptions{GRPCHostPort: hostPort, HTTPHostPort: hostPort, BearerTokenPropagation: true}, - tenancy.NewTenancyManager(&tenancy.Options{}), + tenancy.NewManager(&tenancy.Options{}), opentracing.NoopTracer{}) assert.Nil(t, err) assert.NoError(t, server.Start()) @@ -671,7 +671,7 @@ func TestServerGracefulExit(t *testing.T) { tracer := opentracing.NoopTracer{} server, err := NewServer(flagsSvc.Logger, querySvc, nil, &QueryOptions{GRPCHostPort: hostPort, HTTPHostPort: hostPort}, - tenancy.NewTenancyManager(&tenancy.Options{}), tracer) + tenancy.NewManager(&tenancy.Options{}), tracer) assert.Nil(t, err) assert.NoError(t, server.Start()) go func() { @@ -700,7 +700,7 @@ func TestServerHandlesPortZero(t *testing.T) { tracer := opentracing.NoopTracer{} server, err := NewServer(flagsSvc.Logger, querySvc, nil, &QueryOptions{GRPCHostPort: ":0", HTTPHostPort: ":0"}, - tenancy.NewTenancyManager(&tenancy.Options{}), + tenancy.NewManager(&tenancy.Options{}), tracer) assert.Nil(t, err) assert.NoError(t, server.Start()) @@ -751,7 +751,7 @@ func TestServerHTTPTenancy(t *testing.T) { Enabled: true, }, } - tenancyMgr := tenancy.NewTenancyManager(&serverOptions.Tenancy) + tenancyMgr := tenancy.NewManager(&serverOptions.Tenancy) spanReader := &spanstoremocks.Reader{} dependencyReader := &depsmocks.Reader{} diff --git a/cmd/query/main.go b/cmd/query/main.go index bd9b7e49530..51c374637d8 100644 --- a/cmd/query/main.go +++ b/cmd/query/main.go @@ -125,7 +125,7 @@ func main() { spanReader, dependencyReader, *queryServiceOptions) - tm := tenancy.NewTenancyManager(&queryOpts.Tenancy) + tm := tenancy.NewManager(&queryOpts.Tenancy) server, err := app.NewServer(svc.Logger, queryService, metricsQueryService, queryOpts, tm, tracer) if err != nil { logger.Fatal("Failed to create server", zap.Error(err)) diff --git a/cmd/remote-storage/Dockerfile b/cmd/remote-storage/Dockerfile new file mode 100644 index 00000000000..0da1354b60d --- /dev/null +++ b/cmd/remote-storage/Dockerfile @@ -0,0 +1,16 @@ +ARG base_image +ARG debug_image + +ARG SVC=remote-storage + +FROM $base_image AS release +ARG TARGETARCH +COPY remote-storage-linux-$TARGETARCH /go/bin/remote-storage-linux +EXPOSE 16686/tcp +ENTRYPOINT ["/go/bin/remote-storage-linux"] + +FROM $debug_image AS debug +ARG TARGETARCH=amd64 +COPY remote-storage-debug-linux-$TARGETARCH /go/bin/remote-storage-linux +EXPOSE 12345/tcp 16686/tcp +ENTRYPOINT ["/go/bin/dlv", "exec", "/go/bin/remote-storage-linux", "--headless", "--listen=:12345", "--api-version=2", "--accept-multiclient", "--log", "--"] diff --git a/cmd/remote-storage/app/flags.go b/cmd/remote-storage/app/flags.go new file mode 100644 index 00000000000..c30426bc19e --- /dev/null +++ b/cmd/remote-storage/app/flags.go @@ -0,0 +1,64 @@ +// Copyright (c) 2022 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "flag" + "fmt" + + "github.com/spf13/viper" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" + "github.com/jaegertracing/jaeger/pkg/tenancy" + "github.com/jaegertracing/jaeger/ports" +) + +const ( + flagGRPCHostPort = "grpc.host-port" +) + +var tlsGRPCFlagsConfig = tlscfg.ServerFlagsConfig{ + Prefix: "grpc", +} + +// Options holds configuration for remote-storage service. +type Options struct { + // GRPCHostPort is the host:port address for gRPC server + GRPCHostPort string + // TLSGRPC configures secure transport + TLSGRPC tlscfg.Options + // Tenancy configuration + Tenancy tenancy.Options +} + +// AddFlags adds flags to flag set. +func AddFlags(flagSet *flag.FlagSet) { + flagSet.String(flagGRPCHostPort, ports.PortToHostPort(ports.RemoteStorageGRPC), "The host:port (e.g. 127.0.0.1:17271 or :17271) of the gRPC server") + tlsGRPCFlagsConfig.AddFlags(flagSet) + tenancy.AddFlags(flagSet) +} + +// InitFromViper initializes Options with properties from CLI flags. +func (o *Options) InitFromViper(v *viper.Viper, logger *zap.Logger) (*Options, error) { + o.GRPCHostPort = v.GetString(flagGRPCHostPort) + if tlsGrpc, err := tlsGRPCFlagsConfig.InitFromViper(v); err == nil { + o.TLSGRPC = tlsGrpc + } else { + return o, fmt.Errorf("failed to process gRPC TLS options: %w", err) + } + o.Tenancy = tenancy.InitFromViper(v) + return o, nil +} diff --git a/cmd/remote-storage/app/flags_test.go b/cmd/remote-storage/app/flags_test.go new file mode 100644 index 00000000000..592b70a70a0 --- /dev/null +++ b/cmd/remote-storage/app/flags_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2022 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/pkg/config" +) + +func TestFlags(t *testing.T) { + v, command := config.Viperize(AddFlags) + command.ParseFlags([]string{ + "--grpc.host-port=127.0.0.1:8081", + }) + qOpts, err := new(Options).InitFromViper(v, zap.NewNop()) + require.NoError(t, err) + assert.Equal(t, "127.0.0.1:8081", qOpts.GRPCHostPort) +} + +func TestFailedTLSFlags(t *testing.T) { + v, command := config.Viperize(AddFlags) + err := command.ParseFlags([]string{ + "--grpc.tls.enabled=false", + "--grpc.tls.cert=blah", // invalid unless tls.enabled + }) + require.NoError(t, err) + _, err = new(Options).InitFromViper(v, zap.NewNop()) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to process gRPC TLS options") +} diff --git a/cmd/remote-storage/app/server.go b/cmd/remote-storage/app/server.go new file mode 100644 index 00000000000..b7ccbf596da --- /dev/null +++ b/cmd/remote-storage/app/server.go @@ -0,0 +1,151 @@ +// Copyright (c) 2020 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "fmt" + "net" + + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/reflection" + + "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" + "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/tenancy" + "github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" + "github.com/jaegertracing/jaeger/storage" + "github.com/jaegertracing/jaeger/storage/dependencystore" + "github.com/jaegertracing/jaeger/storage/spanstore" +) + +// Server runs a gRPC server +type Server struct { + logger *zap.Logger + opts *Options + + grpcConn net.Listener + grpcServer *grpc.Server + unavailableChannel chan healthcheck.Status // used to signal to admin server that gRPC server is unavailable +} + +// NewServer creates and initializes Server. +func NewServer(options *Options, storageFactory storage.Factory, tm *tenancy.Manager, logger *zap.Logger) (*Server, error) { + handler, err := createGRPCHandler(storageFactory, logger) + if err != nil { + return nil, err + } + + grpcServer, err := createGRPCServer(options, tm, handler, logger) + if err != nil { + return nil, err + } + + return &Server{ + logger: logger, + opts: options, + grpcServer: grpcServer, + unavailableChannel: make(chan healthcheck.Status), + }, nil +} + +func createGRPCHandler(f storage.Factory, logger *zap.Logger) (*shared.GRPCHandler, error) { + reader, err := f.CreateSpanReader() + if err != nil { + return nil, err + } + writer, err := f.CreateSpanWriter() + if err != nil { + return nil, err + } + depReader, err := f.CreateDependencyReader() + if err != nil { + return nil, err + } + + impl := &shared.GRPCHandlerStorageImpl{ + SpanReader: func() spanstore.Reader { return reader }, + SpanWriter: func() spanstore.Writer { return writer }, + DependencyReader: func() dependencystore.Reader { return depReader }, + StreamingSpanWriter: func() spanstore.Writer { return nil }, + } + + // borrow code from Query service for archive storage + qOpts := &querysvc.QueryServiceOptions{} + // when archive storage not initialized (returns false), the reader/writer will be nil + _ = qOpts.InitArchiveStorage(f, logger) + impl.ArchiveSpanReader = func() spanstore.Reader { return qOpts.ArchiveSpanReader } + impl.ArchiveSpanWriter = func() spanstore.Writer { return qOpts.ArchiveSpanWriter } + + handler := shared.NewGRPCHandler(impl) + return handler, nil +} + +// HealthCheckStatus returns health check status channel a client can subscribe to +func (s Server) HealthCheckStatus() chan healthcheck.Status { + return s.unavailableChannel +} + +func createGRPCServer(opts *Options, tm *tenancy.Manager, handler *shared.GRPCHandler, logger *zap.Logger) (*grpc.Server, error) { + var grpcOpts []grpc.ServerOption + + if opts.TLSGRPC.Enabled { + tlsCfg, err := opts.TLSGRPC.Config(logger) + if err != nil { + return nil, fmt.Errorf("invalid TLS config: %w", err) + } + creds := credentials.NewTLS(tlsCfg) + grpcOpts = append(grpcOpts, grpc.Creds(creds)) + } + if tm.Enabled { + grpcOpts = append(grpcOpts, + grpc.StreamInterceptor(tenancy.NewGuardingStreamInterceptor(tm)), + grpc.UnaryInterceptor(tenancy.NewGuardingUnaryInterceptor(tm)), + ) + } + + server := grpc.NewServer(grpcOpts...) + reflection.Register(server) + handler.Register(server) + + return server, nil +} + +// Start gRPC server concurrently +func (s *Server) Start() error { + listener, err := net.Listen("tcp", s.opts.GRPCHostPort) + if err != nil { + return err + } + s.logger.Info("Starting GRPC server", zap.Stringer("addr", listener.Addr())) + s.grpcConn = listener + go func() { + if err := s.grpcServer.Serve(s.grpcConn); err != nil { + s.logger.Error("GRPC server exited", zap.Error(err)) + } + s.unavailableChannel <- healthcheck.Unavailable + }() + + return nil +} + +// Close stops http, GRPC servers and closes the port listener. +func (s *Server) Close() error { + s.grpcServer.Stop() + s.grpcConn.Close() + s.opts.TLSGRPC.Close() + return nil +} diff --git a/cmd/remote-storage/app/server_test.go b/cmd/remote-storage/app/server_test.go new file mode 100644 index 00000000000..613f09cf26e --- /dev/null +++ b/cmd/remote-storage/app/server_test.go @@ -0,0 +1,427 @@ +// Copyright (c) 2022 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "context" + "errors" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + + "github.com/jaegertracing/jaeger/cmd/flags" + "github.com/jaegertracing/jaeger/internal/grpctest" + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" + "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/tenancy" + "github.com/jaegertracing/jaeger/ports" + "github.com/jaegertracing/jaeger/proto-gen/storage_v1" + depStoreMocks "github.com/jaegertracing/jaeger/storage/dependencystore/mocks" + factoryMocks "github.com/jaegertracing/jaeger/storage/mocks" + spanStoreMocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks" +) + +var testCertKeyLocation = "../../../pkg/config/tlscfg/testdata" + +func TestNewServer_CreateStorageErrors(t *testing.T) { + factory := new(factoryMocks.Factory) + factory.On("CreateSpanReader").Return(nil, errors.New("no reader")).Once() + factory.On("CreateSpanReader").Return(nil, nil) + factory.On("CreateSpanWriter").Return(nil, errors.New("no writer")).Once() + factory.On("CreateSpanWriter").Return(nil, nil) + factory.On("CreateDependencyReader").Return(nil, errors.New("no deps")).Once() + factory.On("CreateDependencyReader").Return(nil, nil) + + f := func() (*Server, error) { + return NewServer( + &Options{GRPCHostPort: ":0"}, + factory, + tenancy.NewManager(&tenancy.Options{}), + zap.NewNop(), + ) + } + _, err := f() + require.Error(t, err) + assert.Contains(t, err.Error(), "no reader") + + _, err = f() + require.Error(t, err) + assert.Contains(t, err.Error(), "no writer") + + _, err = f() + require.Error(t, err) + assert.Contains(t, err.Error(), "no deps") + + s, err := f() + require.NoError(t, err) + err = s.Start() + require.NoError(t, err) + validateGRPCServer(t, s.grpcConn.Addr().String(), s.grpcServer) + + s.grpcConn.Close() // causes logged error + <-s.HealthCheckStatus() +} + +func TestServerStart_BadPortErrors(t *testing.T) { + srv := &Server{ + opts: &Options{ + GRPCHostPort: ":-1", + }, + } + assert.Error(t, srv.Start()) +} + +type storageMocks struct { + factory *factoryMocks.Factory + reader *spanStoreMocks.Reader + writer *spanStoreMocks.Writer + depReader *depStoreMocks.Reader +} + +func newStorageMocks() *storageMocks { + reader := new(spanStoreMocks.Reader) + writer := new(spanStoreMocks.Writer) + depReader := new(depStoreMocks.Reader) + + factory := new(factoryMocks.Factory) + factory.On("CreateSpanReader").Return(reader, nil) + factory.On("CreateSpanWriter").Return(writer, nil) + factory.On("CreateDependencyReader").Return(depReader, nil) + + return &storageMocks{ + factory: factory, + reader: reader, + writer: writer, + depReader: depReader, + } +} + +func TestNewServer_TLSConfigError(t *testing.T) { + tlsCfg := tlscfg.Options{ + Enabled: true, + CertPath: "invalid/path", + KeyPath: "invalid/path", + ClientCAPath: "invalid/path", + } + storageMocks := newStorageMocks() + _, err := NewServer( + &Options{GRPCHostPort: ":8081", TLSGRPC: tlsCfg}, + storageMocks.factory, + tenancy.NewManager(&tenancy.Options{}), + zap.NewNop(), + ) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid TLS config") +} + +func TestCreateGRPCHandler(t *testing.T) { + storageMocks := newStorageMocks() + h, err := createGRPCHandler(storageMocks.factory, zap.NewNop()) + require.NoError(t, err) + + storageMocks.writer.On("WriteSpan", mock.Anything, mock.Anything).Return(errors.New("writer error")) + _, err = h.WriteSpan(context.Background(), &storage_v1.WriteSpanRequest{}) + require.Error(t, err) + assert.Contains(t, err.Error(), "writer error") + + storageMocks.depReader.On("GetDependencies", mock.Anything, mock.Anything).Return(nil, errors.New("deps error")) + _, err = h.GetDependencies(context.Background(), &storage_v1.GetDependenciesRequest{}) + require.Error(t, err) + assert.Contains(t, err.Error(), "deps error") + + err = h.GetArchiveTrace(nil, nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "not implemented") + + _, err = h.WriteArchiveSpan(context.Background(), nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "not implemented") + + err = h.WriteSpanStream(nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "not implemented") +} + +var testCases = []struct { + name string + TLS tlscfg.Options + clientTLS tlscfg.Options + expectError bool + expectClientError bool + expectServerFail bool +}{ + { + name: "should pass with insecure connection", + TLS: tlscfg.Options{ + Enabled: false, + }, + clientTLS: tlscfg.Options{ + Enabled: false, + }, + expectError: false, + expectClientError: false, + expectServerFail: false, + }, + { + name: "should fail with TLS client to untrusted TLS server", + TLS: tlscfg.Options{ + Enabled: true, + CertPath: testCertKeyLocation + "/example-server-cert.pem", + KeyPath: testCertKeyLocation + "/example-server-key.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + ServerName: "example.com", + }, + expectError: true, + expectClientError: true, + expectServerFail: false, + }, + { + name: "should fail with TLS client to trusted TLS server with incorrect hostname", + TLS: tlscfg.Options{ + Enabled: true, + CertPath: testCertKeyLocation + "/example-server-cert.pem", + KeyPath: testCertKeyLocation + "/example-server-key.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: testCertKeyLocation + "/example-CA-cert.pem", + ServerName: "nonEmpty", + }, + expectError: true, + expectClientError: true, + expectServerFail: false, + }, + { + name: "should pass with TLS client to trusted TLS server with correct hostname", + TLS: tlscfg.Options{ + Enabled: true, + CertPath: testCertKeyLocation + "/example-server-cert.pem", + KeyPath: testCertKeyLocation + "/example-server-key.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: testCertKeyLocation + "/example-CA-cert.pem", + ServerName: "example.com", + }, + expectError: false, + expectClientError: false, + expectServerFail: false, + }, + { + name: "should fail with TLS client without cert to trusted TLS server requiring cert", + TLS: tlscfg.Options{ + Enabled: true, + CertPath: testCertKeyLocation + "/example-server-cert.pem", + KeyPath: testCertKeyLocation + "/example-server-key.pem", + ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: testCertKeyLocation + "/example-CA-cert.pem", + ServerName: "example.com", + }, + expectError: false, + expectServerFail: false, + expectClientError: true, + }, + { + name: "should pass with TLS client with cert to trusted TLS server requiring cert", + TLS: tlscfg.Options{ + Enabled: true, + CertPath: testCertKeyLocation + "/example-server-cert.pem", + KeyPath: testCertKeyLocation + "/example-server-key.pem", + ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: testCertKeyLocation + "/example-CA-cert.pem", + ServerName: "example.com", + CertPath: testCertKeyLocation + "/example-client-cert.pem", + KeyPath: testCertKeyLocation + "/example-client-key.pem", + }, + expectError: false, + expectServerFail: false, + expectClientError: false, + }, + { + name: "should fail with TLS client without cert to trusted TLS server requiring cert from a different CA", + TLS: tlscfg.Options{ + Enabled: true, + CertPath: testCertKeyLocation + "/example-server-cert.pem", + KeyPath: testCertKeyLocation + "/example-server-key.pem", + ClientCAPath: testCertKeyLocation + "/wrong-CA-cert.pem", // NB: wrong CA + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: testCertKeyLocation + "/example-CA-cert.pem", + ServerName: "example.com", + CertPath: testCertKeyLocation + "/example-client-cert.pem", + KeyPath: testCertKeyLocation + "/example-client-key.pem", + }, + expectError: false, + expectServerFail: false, + expectClientError: true, + }, +} + +type grpcClient struct { + storage_v1.SpanReaderPluginClient + + conn *grpc.ClientConn +} + +func newGRPCClient(t *testing.T, addr string, creds credentials.TransportCredentials, tm *tenancy.Manager) *grpcClient { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + dialOpts := []grpc.DialOption{ + grpc.WithUnaryInterceptor(tenancy.NewClientUnaryInterceptor(tm)), + } + if creds != nil { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds)) + } else { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + + conn, err := grpc.DialContext(ctx, addr, dialOpts...) + require.NoError(t, err) + + return &grpcClient{ + SpanReaderPluginClient: storage_v1.NewSpanReaderPluginClient(conn), + conn: conn, + } +} + +func TestServerGRPCTLS(t *testing.T) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + serverOptions := &Options{ + GRPCHostPort: ":0", + TLSGRPC: test.TLS, + } + flagsSvc := flags.NewService(ports.QueryAdminHTTP) + flagsSvc.Logger = zap.NewNop() + + storageMocks := newStorageMocks() + expectedServices := []string{"test"} + storageMocks.reader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(expectedServices, nil) + + tm := tenancy.NewManager(&tenancy.Options{Enabled: true}) + server, err := NewServer( + serverOptions, + storageMocks.factory, + tm, + flagsSvc.Logger, + ) + assert.Nil(t, err) + assert.NoError(t, server.Start()) + + var wg sync.WaitGroup + wg.Add(1) + once := sync.Once{} + + go func() { + for s := range server.HealthCheckStatus() { + flagsSvc.HC().Set(s) + if s == healthcheck.Unavailable { + once.Do(wg.Done) + } + } + }() + + var clientError error + var client *grpcClient + + if serverOptions.TLSGRPC.Enabled { + clientTLSCfg, err0 := test.clientTLS.Config(zap.NewNop()) + require.NoError(t, err0) + creds := credentials.NewTLS(clientTLSCfg) + client = newGRPCClient(t, server.grpcConn.Addr().String(), creds, tm) + } else { + client = newGRPCClient(t, server.grpcConn.Addr().String(), nil, tm) + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + ctx = tenancy.WithTenant(ctx, "foo") + res, clientError := client.GetServices(ctx, &storage_v1.GetServicesRequest{}) + + if test.expectClientError { + require.Error(t, clientError) + } else { + require.NoError(t, clientError) + assert.Equal(t, expectedServices, res.Services) + } + require.Nil(t, client.conn.Close()) + server.Close() + wg.Wait() + assert.Equal(t, healthcheck.Unavailable, flagsSvc.HC().Get()) + }) + } +} + +func TestServerHandlesPortZero(t *testing.T) { + flagsSvc := flags.NewService(ports.QueryAdminHTTP) + zapCore, logs := observer.New(zap.InfoLevel) + flagsSvc.Logger = zap.New(zapCore) + storageMocks := newStorageMocks() + + server, err := NewServer( + &Options{GRPCHostPort: ":0"}, + storageMocks.factory, + tenancy.NewManager(&tenancy.Options{}), + flagsSvc.Logger, + ) + require.Nil(t, err) + require.NoError(t, server.Start()) + defer server.Close() + + const line = "Starting GRPC server" + message := logs.FilterMessage(line) + require.Equal(t, 1, message.Len(), "Expected '%s' log message, actual logs: %+v", line, logs) + + onlyEntry := message.All()[0] + hostPort := onlyEntry.ContextMap()["addr"].(string) + validateGRPCServer(t, hostPort, server.grpcServer) +} + +func validateGRPCServer(t *testing.T, hostPort string, server *grpc.Server) { + grpctest.ReflectionServiceValidator{ + HostPort: hostPort, + Server: server, + ExpectedServices: []string{ + "jaeger.storage.v1.SpanReaderPlugin", + "jaeger.storage.v1.SpanWriterPlugin", + "jaeger.storage.v1.DependenciesReaderPlugin", + "jaeger.storage.v1.PluginCapabilities", + "jaeger.storage.v1.ArchiveSpanReaderPlugin", + "jaeger.storage.v1.ArchiveSpanWriterPlugin", + "jaeger.storage.v1.StreamingSpanWriterPlugin", + // "grpc.health.v1.Health", + }, + }.Execute(t) +} diff --git a/cmd/remote-storage/main.go b/cmd/remote-storage/main.go new file mode 100644 index 00000000000..a020504d72d --- /dev/null +++ b/cmd/remote-storage/main.go @@ -0,0 +1,121 @@ +// Copyright (c) 2022 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + _ "go.uber.org/automaxprocs" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/cmd/docs" + "github.com/jaegertracing/jaeger/cmd/env" + "github.com/jaegertracing/jaeger/cmd/flags" + "github.com/jaegertracing/jaeger/cmd/remote-storage/app" + "github.com/jaegertracing/jaeger/cmd/status" + "github.com/jaegertracing/jaeger/pkg/config" + "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/tenancy" + "github.com/jaegertracing/jaeger/pkg/version" + "github.com/jaegertracing/jaeger/plugin/storage" + "github.com/jaegertracing/jaeger/ports" +) + +const serviceName = "jaeger-remote-storage" + +func main() { + svc := flags.NewService(ports.RemoteStorageAdminHTTP) + + if os.Getenv(storage.SpanStorageTypeEnvVar) == "" { + os.Setenv(storage.SpanStorageTypeEnvVar, "memory") + // other storage types default to the same type as SpanStorage + } + storageFactory, err := storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr)) + if err != nil { + log.Fatalf("Cannot initialize storage factory: %v", err) + } + + v := viper.New() + command := &cobra.Command{ + Use: serviceName, + Short: serviceName + " allows sharing single-node storage implementations like memstore or Badger.", + Long: serviceName + ` allows sharing single-node storage implementations like memstore or Badger. It implements Jaeger Remote Storage gRPC API.`, + RunE: func(cmd *cobra.Command, args []string) error { + if err := svc.Start(v); err != nil { + return err + } + logger := svc.Logger // shortcut + baseFactory := svc.MetricsFactory.Namespace(metrics.NSOptions{Name: "jaeger"}) + metricsFactory := baseFactory.Namespace(metrics.NSOptions{Name: "remote-storage"}) + version.NewInfoMetrics(metricsFactory) + + opts, err := new(app.Options).InitFromViper(v, logger) + if err != nil { + logger.Fatal("Failed to parse options", zap.Error(err)) + } + + storageFactory.InitFromViper(v, logger) + if err := storageFactory.Initialize(baseFactory, logger); err != nil { + logger.Fatal("Failed to init storage factory", zap.Error(err)) + } + + tm := tenancy.NewManager(&opts.Tenancy) + server, err := app.NewServer(opts, storageFactory, tm, svc.Logger) + if err != nil { + logger.Fatal("Failed to create server", zap.Error(err)) + } + + go func() { + for s := range server.HealthCheckStatus() { + svc.SetHealthCheckStatus(s) + } + }() + + if err := server.Start(); err != nil { + logger.Fatal("Could not start servers", zap.Error(err)) + } + + svc.RunAndThen(func() { + server.Close() + if err := storageFactory.Close(); err != nil { + logger.Error("Failed to close storage factory", zap.Error(err)) + } + }) + return nil + }, + } + + command.AddCommand(version.Command()) + command.AddCommand(env.Command()) + command.AddCommand(docs.Command(v)) + command.AddCommand(status.Command(v, ports.QueryAdminHTTP)) + + config.AddFlags( + v, + command, + svc.AddFlags, + storageFactory.AddFlags, + app.AddFlags, + ) + + if err := command.Execute(); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } +} diff --git a/cmd/tracegen/README.md b/cmd/tracegen/README.md new file mode 100644 index 00000000000..b9e8af7466c --- /dev/null +++ b/cmd/tracegen/README.md @@ -0,0 +1,20 @@ +# tracegen + +`tracegen` is a utility that can generate a steady flow of simple traces useful for performance tuning. +Traces are produced concurrently from one or more worker goroutines. Run with `-h` to see all cli flags. + +The binary is available from the Releases page, as well as a Docker image: + +```sh +$ docker run jaegertracing/jaeger-tracegen -service abcd -traces 10 +``` + +Notice, however, that by default the generator uses the UDP exporter of `jaeger-client-go`, +which sends data to `localhost`, i.e. inside the networking namespace of the container itself, +which obviously doesn't go anywhere. You can use the environment variables supported by +[jaeger-client-go][env] to instruct the SDK where to send the data, for example to switch +to HTTP by setting `JAEGER_ENDPOINT`. + +See example in the included [docker-compose](./docker-compose.yml) file. + +[env]: https://github.com/jaegertracing/jaeger-client-go#environment-variables diff --git a/cmd/tracegen/docker-compose.yml b/cmd/tracegen/docker-compose.yml new file mode 100644 index 00000000000..c43220e573b --- /dev/null +++ b/cmd/tracegen/docker-compose.yml @@ -0,0 +1,16 @@ +version: '2' + +services: + jaeger: + image: jaegertracing/all-in-one:latest + ports: + - '16686:16686' + + tracegen: + image: jaegertracing/jaeger-tracegen:latest + environment: + - JAEGER_AGENT_HOST=jaeger + - JAEGER_AGENT_PORT=6831 + command: ["-duration", "10s", "-workers", "3", "-pause", "250ms"] + depends_on: + - jaeger diff --git a/crossdock/docker-compose.yml b/crossdock/docker-compose.yml index 667fa95de4d..4bcfd782ef6 100644 --- a/crossdock/docker-compose.yml +++ b/crossdock/docker-compose.yml @@ -40,7 +40,7 @@ services: ports: - "8080-8082" depends_on: -# UDP sender needs to know agent's address + # UDP sender needs to know agent's address - jaeger-agent python: diff --git a/crossdock/jaeger-docker-compose.yml b/crossdock/jaeger-docker-compose.yml index 84218978456..fefc3714864 100644 --- a/crossdock/jaeger-docker-compose.yml +++ b/crossdock/jaeger-docker-compose.yml @@ -1,33 +1,46 @@ version: '2' services: + jaeger-remote-storage: + image: jaegertracing/jaeger-remote-storage + command: + - "--log-level=debug" + environment: + - SPAN_STORAGE_TYPE=memory + ports: + - "17271:17271" + jaeger-collector: image: jaegertracing/jaeger-collector - command: ["--es.num-shards=1", "--es.num-replicas=0", "--es.server-urls=http://elasticsearch:9200", "--collector.zipkin.host-port=:9411"] + command: + - "--grpc-storage.server=jaeger-remote-storage:17271" + - "--collector.zipkin.host-port=:9411" + - "--log-level=debug" ports: - "14269" - "14268:14268" - "14250" - "9411:9411" environment: - - SPAN_STORAGE_TYPE=elasticsearch + - SPAN_STORAGE_TYPE=grpc-plugin - LOG_LEVEL=debug restart: on-failure depends_on: - - elasticsearch + - jaeger-remote-storage jaeger-query: image: jaegertracing/jaeger-query - command: ["--es.num-shards=1", "--es.num-replicas=0", "--es.server-urls=http://elasticsearch:9200"] + command: + - "--grpc-storage.server=jaeger-remote-storage:17271" + - "--log-level=debug" ports: - "16686:16686" - "16687" environment: - - SPAN_STORAGE_TYPE=elasticsearch - - LOG_LEVEL=debug + - SPAN_STORAGE_TYPE=grpc-plugin restart: on-failure depends_on: - - elasticsearch + - jaeger-remote-storage jaeger-agent: image: jaegertracing/jaeger-agent @@ -42,12 +55,3 @@ services: restart: on-failure depends_on: - jaeger-collector - - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.3 - environment: - - discovery.type=single-node - ports: - - "9200:9200/tcp" - - diff --git a/docker-compose/kafka/README.md b/docker-compose/kafka/README.md new file mode 100644 index 00000000000..0f18aad2e11 --- /dev/null +++ b/docker-compose/kafka/README.md @@ -0,0 +1,23 @@ +# Sample configuration with Kafka + +This `docker-compose` environment provides a sample configuration of Jaeger depoyment utilizing collector-Kafka-injester pipeline. Storage is provided by the `jageer-remote-storage` service running memstore. + +Jaeger UI can be accessed at http://localhost:16686/, as usual, and refreshing the screen should produce internal traces. + +```mermaid +graph LR + C[jaeger-collector] --> KafkaBroker + KafkaBroker --> I[jaeger-ingester] + I --> S[jaeger-remote-storage] + UI[jaeger-query
Jaeger UI] --> S + S --> MemStore + KafkaBroker --> ZooKeeper + subgraph Kafka + KafkaBroker + ZooKeeper + end + subgraph Shared Storage + S + MemStore + end +``` diff --git a/docker-compose/kafka/docker-compose.yml b/docker-compose/kafka/docker-compose.yml new file mode 100644 index 00000000000..02922cd95a4 --- /dev/null +++ b/docker-compose/kafka/docker-compose.yml @@ -0,0 +1,136 @@ +version: "2.1" + +services: + zookeeper: + image: bitnami/zookeeper + ports: + - 2181:2181 + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + kafka: + image: 'bitnami/kafka:latest' + ports: + - '9092:9092' + environment: + - KAFKA_BROKER_ID=1 + - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 + - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 + - ALLOW_PLAINTEXT_LISTENER=yes + - KAFKA_LISTENERS=INTERNAL://0.0.0.0:9092,OUTSIDE://0.0.0.0:9094 + - KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafka:9092,OUTSIDE://localhost:9094 + - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,OUTSIDE:PLAINTEXT + - KAFKA_INTER_BROKER_LISTENER_NAME=INTERNAL + restart: always + depends_on: + - zookeeper + links: + - zookeeper + healthcheck: + test: ["CMD-SHELL", "kafka-topics.sh --list --bootstrap-server 127.0.0.1:9092"] + interval: 5s + timeout: 5s + retries: 3 + start_period: 5s + + jaeger-remote-storage: + image: jaegertracing/jaeger-remote-storage + ports: + - 17271:17271 + environment: + - SPAN_STORAGE_TYPE=memory + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:17270/ || exit 1"] + interval: 5s + timeout: 5s + retries: 3 + + jaeger-collector: + image: jaegertracing/jaeger-collector + command: + - "--collector.otlp.enabled=true" + - "--log-level=debug" + ports: + - 4318:4318 + - 14250:14250 + environment: + - SPAN_STORAGE_TYPE=kafka + - KAFKA_PRODUCER_BROKERS=kafka:9092 + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:14269/ || exit 1"] + interval: 5s + timeout: 5s + retries: 3 + depends_on: + kafka: + condition: service_healthy + links: + - kafka + + jaeger-ingester: + image: jaegertracing/jaeger-ingester + command: + - "--grpc-storage.server=jaeger-remote-storage:17271" + - "--log-level=debug" + environment: + - SPAN_STORAGE_TYPE=grpc-plugin + - KAFKA_CONSUMER_BROKERS=kafka:9092 + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:14270/ || exit 1"] + interval: 5s + timeout: 5s + retries: 3 + depends_on: + kafka: + condition: service_healthy + jaeger-remote-storage: + condition: service_healthy + jaeger-collector: + condition: service_healthy + links: + - kafka + - jaeger-remote-storage + + jaeger-agent: + image: jaegertracing/jaeger-agent + command: + - "--reporter.grpc.host-port=jaeger-collector:14250" + - "--log-level=debug" + ports: + - "6831:6831/udp" + - "6832:6832/udp" + - "5778:5778" + restart: on-failure + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:14271/ || exit 1"] + interval: 5s + timeout: 5s + retries: 3 + depends_on: + jaeger-collector: + condition: service_healthy + links: + - jaeger-collector + + jaeger-query: + image: jaegertracing/jaeger-query + command: + - "--grpc-storage.server=jaeger-remote-storage:17271" + - "--log-level=debug" + environment: + - SPAN_STORAGE_TYPE=grpc-plugin + - JAEGER_AGENT_HOST=jaeger-agent + ports: + - "16686:16686" + - "16687" + restart: on-failure + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:16687/ || exit 1"] + interval: 5s + timeout: 5s + retries: 3 + depends_on: + jaeger-remote-storage: + condition: service_healthy + links: + - jaeger-agent + - jaeger-remote-storage diff --git a/docker-compose/monitor/README.md b/docker-compose/monitor/README.md index 1efe4a914fa..edcad461412 100644 --- a/docker-compose/monitor/README.md +++ b/docker-compose/monitor/README.md @@ -4,12 +4,14 @@ Service Performance Monitoring (SPM) is an opt-in feature introduced to Jaeger t The motivation for providing this environment is to allow developers to either test Jaeger UI or their own applications against jaeger-query's metrics query API, as well as a quick and simple way for users to bring up the entire stack required to visualize RED metrics from simulated traces (or their own). -This environment consists four backend components: +This environment consists the following backend components: - [MicroSim](https://github.com/yurishkuro/microsim): a program to simulate traces. - [Jaeger All-in-one](https://www.jaegertracing.io/docs/1.24/getting-started/#all-in-one): the full Jaeger stack in a single container image. - [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/): vendor agnostic integration layer for traces and metrics. Its main role in this particular development environment is to receive Jaeger spans, forward these spans untouched to Jaeger All-in-one while simultaneously aggregating metrics out of this span data. To learn more about span metrics aggregation, please refer to the [spanmetrics processor documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/spanmetricsprocessor). - [Prometheus](https://prometheus.io/): a metrics collection and query engine, used to scrape metrics computed by OpenTelemetry Collector, and presents an API for Jaeger All-in-one to query these metrics. +- [Grafana](https://grafana.com/): a metrics visualization, analytics & monitoring solution supporting multiple metrics databases. + The following diagram illustrates the relationship between these components: @@ -24,7 +26,11 @@ docker-compose up docker-compose down ``` -**Tip:** Let the application run for a couple of minutes to ensure there is enough time series data to plot in the dashboard. Navigate to Jaeger UI at http://localhost:16686/ and inspect the Monitor tab. Select `redis` service from the dropdown to see more than one endpoint. +**Tips:** +- Let the application run for a couple of minutes to ensure there is enough time series data to plot in the dashboard. +- Navigate to Jaeger UI at http://localhost:16686/ and inspect the Monitor tab. Select `redis` service from the dropdown to see more than one endpoint. +- To visualize the raw metrics stored on the Prometheus server (for debugging and local development use cases), a Grafana server is included in the docker-compose config, which is preconfigured to read metrics from the Prometheus server. + To access Grafana, navigate to http://localhost:3000/, click on the "Explore" and click the "Select metric" dropdown then select `calls_total`, for example. **Warning:** The included [docker-compose.yml](./docker-compose.yml) file uses the `latest` version of Jaeger and other components. If your local Docker registry already contains older versions, which may still be tagged as `latest`, you may want to delete those images before running the full set, to ensure consistent behavior: @@ -32,6 +38,7 @@ docker-compose down docker rmi -f jaegertracing/all-in-one:latest docker rmi -f otel/opentelemetry-collector-contrib:latest docker rmi -f prom/prometheus:latest +docker rmi -f grafana/grafana:latest ``` ## Example 1 diff --git a/docker-compose/monitor/datasource.yml b/docker-compose/monitor/datasource.yml new file mode 100644 index 00000000000..91a1c80c6ca --- /dev/null +++ b/docker-compose/monitor/datasource.yml @@ -0,0 +1,8 @@ +apiVersion: 1 +datasources: +- name: Prometheus + type: prometheus + url: http://prometheus:9090 + isDefault: true + access: proxy + editable: true diff --git a/docker-compose/monitor/docker-compose.yml b/docker-compose/monitor/docker-compose.yml index 8fff1e9fded..5a8fd05ffdb 100644 --- a/docker-compose/monitor/docker-compose.yml +++ b/docker-compose/monitor/docker-compose.yml @@ -23,6 +23,8 @@ services: volumes: - "./otel-collector-config.yml:/etc/otelcol/otel-collector-config.yml" command: --config /etc/otelcol/otel-collector-config.yml + ports: + - "14278:14278" depends_on: - jaeger microsim: @@ -40,5 +42,19 @@ services: - "./prometheus.yml:/etc/prometheus/prometheus.yml" ports: - "9090:9090" + grafana: + networks: + - backend + image: grafana/grafana:latest + volumes: + - ./grafana.ini:/etc/grafana/grafana.ini + - ./datasource.yml:/etc/grafana/provisioning/datasources/datasource.yaml + environment: + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_DISABLE_LOGIN_FORM=true + ports: + - 3000:3000 + networks: backend: diff --git a/docker-compose/monitor/grafana.ini b/docker-compose/monitor/grafana.ini new file mode 100644 index 00000000000..e5c19f5e4f7 --- /dev/null +++ b/docker-compose/monitor/grafana.ini @@ -0,0 +1,1328 @@ +##################### Grafana Configuration Defaults ##################### +# +# Do not modify this file in grafana installs +# + +# possible values : production, development +app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +instance_name = ${HOSTNAME} + +# force migration will run migrations that might cause dataloss +force_migration = false + +#################################### Paths ############################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +data = data + +# Temporary files in `data` directory older than given duration will be removed +temp_data_lifetime = 24h + +# Directory where grafana can store logs +logs = data/log + +# Directory where grafana will automatically scan and look for plugins +plugins = data/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +provisioning = conf/provisioning + +#################################### Server ############################## +[server] +# Protocol (http, https, h2, socket) +protocol = http + +# The ip address to bind to, empty will bind to all interfaces +http_addr = + +# The http port to use +http_port = 3000 + +# The public facing domain name used to access grafana from a browser +domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +enforce_domain = false + +# The full public facing url +root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +serve_from_sub_path = false + +# Log web requests +router_logging = false + +# the path relative working path +static_root_path = public + +# enable gzip +enable_gzip = false + +# https certs & key file +cert_file = +cert_key = + +# Unix socket path +socket = /tmp/grafana.sock + +# CDN Url +cdn_url = + +# Sets the maximum time in minutes before timing out read of an incoming request and closing idle connections. +# `0` means there is no timeout for reading the request. +read_timeout = 0 + +#################################### Database ############################ +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url property. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +type = sqlite3 +host = 127.0.0.1:3306 +name = grafana +user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +password = +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +url = + +# Max idle conn setting default is 2 +max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +log_queries = + +# For "postgres", use either "disable", "require" or "verify-full" +# For "mysql", use either "true", "false", or "skip-verify". +ssl_mode = disable + +# Database drivers may support different transaction isolation levels. +# Currently, only "mysql" driver supports isolation levels. +# If the value is empty - driver's default isolation level is applied. +# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE". +isolation_level = + +ca_cert_path = +client_key_path = +client_cert_path = +server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +path = grafana.db + +# For "sqlite3" only. cache mode setting used for connecting to the database +cache_mode = private + +# For "mysql" only if migrationLocking feature toggle is set. How many seconds to wait before failing to lock the database for the migrations, default is 0. +locking_attempt_timeout_sec = 0 + +#################################### Cache server ############################# +[remote_cache] +# Either "redis", "memcached" or "database" default is "database" +type = database + +# cache connectionstring options +# database: will use Grafana primary database. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# memcache: 127.0.0.1:11211 +connstr = + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +logging = false + +# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds. +# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. +timeout = 30 + +# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds. +dialTimeout = 10 + +# How many seconds the data proxy waits before sending a keepalive request. +keep_alive_seconds = 30 + +# How many seconds the data proxy waits for a successful TLS Handshake before timing out. +tls_handshake_timeout_seconds = 10 + +# How many seconds the data proxy will wait for a server's first response headers after +# fully writing the request headers if the request has an "Expect: 100-continue" +# header. A value of 0 will result in the body being sent immediately, without +# waiting for the server to approve. +expect_continue_timeout_seconds = 1 + +# Optionally limits the total number of connections per host, including connections in the dialing, +# active, and idle states. On limit violation, dials will block. +# A value of zero (0) means no limit. +max_conns_per_host = 0 + +# The maximum number of idle connections that Grafana will keep alive. +max_idle_connections = 100 + +# How many seconds the data proxy keeps an idle connection open before timing out. +idle_conn_timeout_seconds = 90 + +# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request. +send_user_header = false + +# Limit the amount of bytes that will be read/accepted from responses of outgoing HTTP requests. +response_limit = 0 + +# Limits the number of rows that Grafana will process from SQL data sources. +row_limit = 1000000 + +#################################### Analytics ########################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +reporting_enabled = true + +# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs +reporting_distributor = grafana-labs + +# Set to false to disable all checks to https://grafana.com +# for new versions of grafana. The check is used +# in some UI views to notify that a grafana update exists. +# This option does not cause any auto updates, nor send any information +# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version. +check_for_updates = true + +# Set to false to disable all checks to https://grafana.com +# for new versions of plugins. The check is used +# in some UI views to notify that a plugin update exists. +# This option does not cause any auto updates, nor send any information +# only a GET request to https://grafana.com to get the latest versions. +check_for_plugin_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +google_analytics_ua_id = + +# Google Analytics 4 tracking code, only enabled if you specify an id here +google_analytics_4_id = + +# Google Tag Manager ID, only enabled if you specify an id here +google_tag_manager_id = + +# Rudderstack write key, enabled only if rudderstack_data_plane_url is also set +rudderstack_write_key = + +# Rudderstack data plane url, enabled only if rudderstack_write_key is also set +rudderstack_data_plane_url = + +# Rudderstack SDK url, optional, only valid if rudderstack_write_key and rudderstack_data_plane_url is also set +rudderstack_sdk_url = + +# Rudderstack Config url, optional, used by Rudderstack SDK to fetch source config +rudderstack_config_url = + +# Application Insights connection string. Specify an URL string to enable this feature. +application_insights_connection_string = + +# Optional. Specifies an Application Insights endpoint URL where the endpoint string is wrapped in backticks ``. +application_insights_endpoint_url = + +# Controls if the UI contains any links to user feedback forms +feedback_links_enabled = true + +#################################### Security ############################ +[security] +# disable creation of admin user on first start of grafana +disable_initial_admin_creation = false + +# default admin user, created on startup +admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +admin_password = admin + +# default admin email, created on startup +admin_email = admin@localhost + +# used for signing +secret_key = SW2YcwTIb9zpOOhoPsMm + +# current key provider used for envelope encryption, default to static value specified by secret_key +encryption_provider = secretKey.v1 + +# list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1 +available_encryption_providers = + +# disable gravatar profile images +disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +data_source_proxy_whitelist = + +# disable protection against brute force login attempts +disable_brute_force_login_protection = false + +# set to true if you host Grafana behind HTTPS. default is false. +cookie_secure = false + +# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" +cookie_samesite = lax + +# set to true if you want to allow browsers to render Grafana in a ,