diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c712849e9..6b13de2d44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: container: # Whenever the Go version is updated here, .promu.yml # should also be updated. - image: quay.io/prometheus/golang-builder:1.25-base + image: quay.io/prometheus/golang-builder:1.26-base steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -34,7 +34,7 @@ jobs: name: More Go tests runs-on: ubuntu-latest container: - image: quay.io/prometheus/golang-builder:1.25-base + image: quay.io/prometheus/golang-builder:1.26-base steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -55,8 +55,6 @@ jobs: env: # Enforce the Go version. GOTOOLCHAIN: local - # TODO: remove once 1.25 is the min version. - GOEXPERIMENT: synctest container: # The go version in this image should be N-1 wrt test_go. image: quay.io/prometheus/golang-builder:1.25-base @@ -67,9 +65,6 @@ jobs: - run: make build # Don't run NPM build; don't run race-detector. - run: make test GO_ONLY=1 test-flags="" - # TODO: remove once 1.25 is the min version. - # ensure we can build without the tag. - - run: GOEXPERIMENT="" make build test_ui: name: UI tests @@ -77,7 +72,7 @@ jobs: # Whenever the Go version is updated here, .promu.yml # should also be updated. container: - image: quay.io/prometheus/golang-builder:1.25-base + image: quay.io/prometheus/golang-builder:1.26-base steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -104,7 +99,7 @@ jobs: persist-credentials: false - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: - go-version: 1.25.x + go-version: 1.26.x - run: | $TestTargets = go list ./... | Where-Object { $_ -NotMatch "(github.com/prometheus/prometheus/config|github.com/prometheus/prometheus/web)"} go test $TestTargets -vet=off -v @@ -116,7 +111,7 @@ jobs: # Whenever the Go version is updated here, .promu.yml # should also be updated. container: - image: quay.io/prometheus/golang-builder:1.25-base + image: quay.io/prometheus/golang-builder:1.26-base steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -227,7 +222,7 @@ jobs: name: Check generated parser runs-on: ubuntu-latest container: - image: quay.io/prometheus/golang-builder:1.25-base + image: quay.io/prometheus/golang-builder:1.26-base steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -250,7 +245,7 @@ jobs: - name: Install Go uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: - go-version: 1.25.x + go-version: 1.26.x - name: Install snmp_exporter/generator dependencies run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' @@ -265,8 +260,7 @@ jobs: - name: Lint with slicelabels uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: - # goexperiment.synctest to ensure we don't miss files that depend on it. - args: --verbose --build-tags=slicelabels,goexperiment.synctest + args: --verbose --build-tags=slicelabels version: ${{ steps.golangci-lint-version.outputs.version }} - name: Lint with dedupelabels uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index f19e0ba609..fe7a977cc9 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -19,7 +19,7 @@ jobs: - name: Install Go uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: - go-version: 1.25.x + go-version: 1.26.x - name: Run Fuzzing run: go test -fuzz=${{ matrix.fuzz_test }}$ -fuzztime=5m ./util/fuzzing continue-on-error: true diff --git a/.golangci.yml b/.golangci.yml index ff37050211..8d5f2ac95b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -129,6 +129,9 @@ linters: # Disable this check for now since it introduces too many changes in our existing codebase. # See https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_omitzero for more details. - omitzero + # Disable newexpr check for now since it introduces too many changes in our existing codebase. + # To be re-enabled as a part of https://github.com/prometheus/prometheus/issues/18066. + - newexpr perfsprint: # Optimizes even if it requires an int or uint type cast. int-conversion: true diff --git a/.promu.yml b/.promu.yml index d5205b4fdf..0e1747a62b 100644 --- a/.promu.yml +++ b/.promu.yml @@ -1,7 +1,7 @@ go: # Whenever the Go version is updated here, # .github/workflows should also be updated. - version: 1.25 + version: 1.26 repository: path: github.com/prometheus/prometheus build: diff --git a/CHANGELOG.md b/CHANGELOG.md index a1afb0af59..eabcf6d9fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,56 @@ # Changelog +## 3.10.0 / 2026-02-24 + +Prometheus now offers a distroless Docker image variant alongside the default +busybox image. The distroless variant provides enhanced security with a minimal +base image, uses UID/GID 65532 (nonroot) instead of nobody, and removes the +VOLUME declaration. Both variants are available with `-busybox` and `-distroless` +tag suffixes (e.g., `prom/prometheus:latest-busybox`, `prom/prometheus:latest-distroless`). +The busybox image remains the default with no suffix for backwards compatibility +(e.g., `prom/prometheus:latest` points to the busybox variant). + +For users migrating existing **named** volumes from the busybox image to the distroless variant, the ownership can be adjusted with: +``` +docker run --rm -v prometheus-data:/prometheus alpine chown -R 65532:65532 /prometheus +``` +Then, the container can be started with the old volume with: +``` +docker run -v prometheus-data:/prometheus prom/prometheus:latest-distroless +``` +User migrating from bind mounts might need to ajust permissions too, depending on their setup. + +- [CHANGE] Alerting: Add `alertmanager` dimension to following metrics: `prometheus_notifications_dropped_total`, `prometheus_notifications_queue_capacity`, `prometheus_notifications_queue_length`. #16355 +- [CHANGE] UI: Hide expanded alert annotations by default, enabling more information density on the `/alerts` page. #17611 +- [FEATURE] AWS SD: Add MSK Role. #17600 +- [FEATURE] PromQL: Add `fill()` / `fill_left()` / `fill_right()` binop modifiers for specifying default values for missing series. #17644 +- [FEATURE] Web: Add OpenAPI 3.2 specification for the HTTP API at `/api/v1/openapi.yaml`. #17825 +- [FEATURE] Dockerfile: Add distroless image variant using UID/GID 65532 and no VOLUME declaration. Busybox image remains default. #17876 +- [FEATURE] Web: Add on-demand wall time profiling under `/debug/pprof/fgprof`. #18027 +- [ENHANCEMENT] PromQL: Add more detail to histogram quantile monotonicity info annotations. #15578 +- [ENHANCEMENT] Alerting: Independent alertmanager sendloops. #16355 +- [ENHANCEMENT] TSDB: Experimental support for early compaction of stale series in the memory with configurable threshold `stale_series_compaction_threshold` in the config file. #16929 +- [ENHANCEMENT] Service Discovery: Service discoveries are now removable from the Prometheus binary through the Go build tag `remove_all_sd` and individual service discoveries can be re-added with the build tags `enable__sd`. Users can build a custom Prometheus with only the necessary SDs for a smaller binary size. #17736 +- [ENHANCEMENT] Promtool: Support promql syntax features `promql-duration-expr` and `promql-extended-range-selectors`. #17926 +- [PERF] PromQL: Avoid unnecessary label extraction in PromQL functions. #17676 +- [PERF] PromQL: Improve performance of regex matchers like `.*-.*-.*`. #17707 +- [PERF] OTLP: Add label caching for OTLP-to-Prometheus conversion to reduce allocations and improve latency. #17860 +- [PERF] API: Compute `/api/v1/targets/relabel_steps` in a single pass instead of re-running relabeling for each prefix. #17969 +- [PERF] tsdb: Optimize LabelValues intersection performance for matchers. #18069 +- [BUGFIX] PromQL: Prevent query strings containing only UTF-8 continuation bytes from crashing Prometheus. #17735 +- [BUGFIX] Web: Fix missing `X-Prometheus-Stopping` header for `/-/ready` endpoint in `NotReady` state. #17795 +- [BUGFIX] PromQL: Fix PromQL `info()` function returning empty results when filtering by a label that exists on both the input metric and `target_info`. #17817 +- [BUGFIX] TSDB: Fix a bug during exemplar buffer grow/shrink that could cause exemplars to be incorrectly discarded. #17863 +- [BUGFIX] UI: Fix broken graph display after page reload, due to broken Y axis min encoding/decoding. #17869 +- [BUGFIX] TSDB: Fix memory leaks in buffer pools by clearing reference fields (Labels, Histogram pointers, metadata strings) before returning buffers to pools. #17879 +- [BUGFIX] PromQL: info function: fix series without identifying labels not being returned. #17898 +- [BUGFIX] OTLP: Filter `__name__` from OTLP attributes to prevent duplicate labels. #17917 +- [BUGFIX] TSDB: Fix division by zero when computing stale series ratio with empty head. #17952 +- [BUGFIX] OTLP: Fix potential silent data loss for sum metrics. #17954 +- [BUGFIX] PromQL: Fix smoothed interpolation across counter resets. #17988 +- [BUGFIX] PromQL: Fix panic with `@` modifier on empty ranges. #18020 +- [BUGFIX] PromQL: Fix `avg_over_time` for a single native histogram. #18058 + ## 3.9.1 / 2026-01-07 - [BUGFIX] Agent: fix crash shortly after startup from invalid type of object. #17802 diff --git a/CODEOWNERS b/CODEOWNERS index 2c5dedbffa..d09cbfe3c9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -22,6 +22,7 @@ /discovery/kubernetes @prometheus/default-maintainers @brancz /discovery/stackit @prometheus/default-maintainers @jkroepke /discovery/aws/ @prometheus/default-maintainers @matt-gp @sysadmind +/discovery/consul @prometheus/default-maintainers @mrvarmazyar # Pending # https://github.com/prometheus/prometheus/pull/15212#issuecomment-3575225179 # /discovery/aliyun @prometheus/default-maintainers @KeyOfSpectator diff --git a/MAINTAINERS.md b/MAINTAINERS.md index ae61059af5..d099221eb9 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -7,6 +7,7 @@ General maintainers: * Ayoub Mrini (ayoubmrini424@gmail.com / @machine424) * Julien Pivotto (roidelapluie@prometheus.io / @roidelapluie) * György Krajcsovits ( / @krajorama) +* Bartłomiej Płotka ( / @bwplotka) Maintainers for specific parts of the codebase: * `cmd` @@ -14,12 +15,13 @@ Maintainers for specific parts of the codebase: * `discovery` * `k8s`: Frederic Branczyk ( / @brancz) * `stackit`: Jan-Otto Kröpke ( / @jkroepke) + * `consul`: Mohammad Varmazyar ( / @mrvarmazyar) * `documentation` * `prometheus-mixin`: Matthias Loibl ( / @metalmatze) * `storage` - * `remote`: Callum Styan ( / @cstyan), Bartłomiej Płotka ( / @bwplotka), Tom Wilkie (tom.wilkie@gmail.com / @tomwilkie), Alex Greenbank ( / @alexgreenbank) + * `remote`: Callum Styan ( / @cstyan), Tom Wilkie (tom.wilkie@gmail.com / @tomwilkie), Alex Greenbank ( / @alexgreenbank) * `otlptranslator`: Arthur Silva Sens ( / @ArthurSens), Arve Knudsen ( / @aknuds1), Jesús Vázquez ( / @jesusvazquez) -* `tsdb`: Ganesh Vernekar ( / @codesome), Bartłomiej Płotka ( / @bwplotka), Jesús Vázquez ( / @jesusvazquez) +* `tsdb`: Ganesh Vernekar ( / @codesome), Jesús Vázquez ( / @jesusvazquez) * `web` * `ui`: Julius Volz ( / @juliusv) * `module`: Augustin Husson ( / @nexucis) diff --git a/Makefile b/Makefile index ad4b90f020..59f0a71259 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,8 @@ # Needs to be defined before including Makefile.common to auto-generate targets DOCKER_ARCHS ?= amd64 armv7 arm64 ppc64le riscv64 s390x +DOCKERFILE_ARCH_EXCLUSIONS ?= Dockerfile.distroless:riscv64 +DOCKER_REGISTRY_ARCH_EXCLUSIONS ?= quay.io:riscv64 UI_PATH = web/ui UI_NODE_MODULES_PATH = $(UI_PATH)/node_modules diff --git a/Makefile.common b/Makefile.common index d19d390d37..cce3ef1d16 100644 --- a/Makefile.common +++ b/Makefile.common @@ -109,6 +109,24 @@ endif # Build variant:dockerfile pairs for shell iteration. DOCKERFILE_VARIANTS_WITH_NAMES := $(foreach df,$(DOCKERFILE_VARIANTS),$(call dockerfile_variant,$(df)):$(df)) +# Shell helper to check whether a dockerfile/arch pair is excluded. +define dockerfile_arch_is_excluded +case " $(DOCKERFILE_ARCH_EXCLUSIONS) " in \ + *" $$dockerfile:$(1) "*) true ;; \ + *) false ;; \ +esac +endef + +# Shell helper to check whether a registry/arch pair is excluded. +# Extracts registry from DOCKER_REPO (e.g., quay.io/prometheus -> quay.io) +define registry_arch_is_excluded +registry=$$(echo "$(DOCKER_REPO)" | cut -d'/' -f1); \ +case " $(DOCKER_REGISTRY_ARCH_EXCLUSIONS) " in \ + *" $$registry:$(1) "*) true ;; \ + *) false ;; \ +esac +endef + BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) @@ -250,6 +268,10 @@ $(BUILD_DOCKER_ARCHS): common-docker-%: @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ dockerfile=$${variant#*:}; \ variant_name=$${variant%%:*}; \ + if $(call dockerfile_arch_is_excluded,$*); then \ + echo "Skipping $$variant_name variant for linux-$* (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ distroless_arch="$*"; \ if [ "$*" = "armv7" ]; then \ distroless_arch="arm"; \ @@ -284,6 +306,14 @@ $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ dockerfile=$${variant#*:}; \ variant_name=$${variant%%:*}; \ + if $(call dockerfile_arch_is_excluded,$*); then \ + echo "Skipping push for $$variant_name variant on linux-$* (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + if $(call registry_arch_is_excluded,$*); then \ + echo "Skipping push for $$variant_name variant on linux-$* to $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ echo "Pushing $$variant_name variant for linux-$*"; \ docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ @@ -311,6 +341,14 @@ $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ dockerfile=$${variant#*:}; \ variant_name=$${variant%%:*}; \ + if $(call dockerfile_arch_is_excluded,$*); then \ + echo "Skipping tag for $$variant_name variant on linux-$* (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + if $(call registry_arch_is_excluded,$*); then \ + echo "Skipping tag for $$variant_name variant on linux-$* for $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ echo "Tagging $$variant_name variant for linux-$* as latest"; \ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest-$$variant_name"; \ @@ -330,23 +368,87 @@ common-docker-manifest: variant_name=$${variant%%:*}; \ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ echo "Creating manifest for $$variant_name variant"; \ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name); \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + if $(call dockerfile_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for $$variant_name (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + if $(call registry_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for $$variant_name on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping manifest for $$variant_name variant (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" $$refs; \ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ fi; \ if [ "$$dockerfile" = "Dockerfile" ]; then \ echo "Creating default variant ($$variant_name) manifest"; \ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG)); \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + if $(call dockerfile_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for default variant (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + if $(call registry_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for default variant on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping default variant manifest (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $$refs; \ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)"; \ fi; \ if [ "$(DOCKER_IMAGE_TAG)" = "latest" ]; then \ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ echo "Creating manifest for $$variant_name variant version tag"; \ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name); \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + if $(call dockerfile_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for $$variant_name version tag (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + if $(call registry_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for $$variant_name version tag on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping version-tag manifest for $$variant_name variant (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name" $$refs; \ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ fi; \ if [ "$$dockerfile" = "Dockerfile" ]; then \ echo "Creating default variant version tag manifest"; \ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):v$(DOCKER_MAJOR_VERSION_TAG)); \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + if $(call dockerfile_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for default variant version tag (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + if $(call registry_arch_is_excluded,$$arch); then \ + echo " Skipping $$arch for default variant version tag on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ + continue; \ + fi; \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping default variant version-tag manifest (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)" $$refs; \ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)"; \ fi; \ fi; \ diff --git a/VERSION b/VERSION index 6bd10744ae..30291cba22 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.9.1 +3.10.0 diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 8bdd1b0d6e..5cdda75f9a 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -1599,7 +1599,7 @@ type reloader struct { reloader func(*config.Config) error } -func reloadConfig(filename string, enableExemplarStorage bool, logger *slog.Logger, noStepSuqueryInterval *safePromQLNoStepSubqueryInterval, callback func(bool), rls ...reloader) (err error) { +func reloadConfig(filename string, enableExemplarStorage bool, logger *slog.Logger, noStepSubqueryInterval *safePromQLNoStepSubqueryInterval, callback func(bool), rls ...reloader) (err error) { start := time.Now() timingsLogger := logger logger.Info("Loading configuration file", "filename", filename) @@ -1640,8 +1640,7 @@ func reloadConfig(filename string, enableExemplarStorage bool, logger *slog.Logg } updateGoGC(conf, logger) - - noStepSuqueryInterval.Set(conf.GlobalConfig.EvaluationInterval) + noStepSubqueryInterval.Set(conf.GlobalConfig.EvaluationInterval) timingsLogger.Info("Completed loading of configuration file", "filename", filename, "totalDuration", time.Since(start)) return nil } diff --git a/compliance/go.mod b/compliance/go.mod index efc9342375..81c5450ff3 100644 --- a/compliance/go.mod +++ b/compliance/go.mod @@ -1,6 +1,6 @@ module compliance -go 1.25.5 +go 1.25.0 require github.com/prometheus/compliance/remotewrite v0.0.0-20260223092825-818283e1171e diff --git a/config/config_test.go b/config/config_test.go index 43c56a501f..776cf8d7e8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -2316,7 +2316,7 @@ var expectedErrors = []struct { }, { filename: "kubernetes_selectors_pod.bad.yml", - errMsg: "pod role supports only pod selectors", + errMsg: "pod role supports only pod, node selectors", }, { filename: "kubernetes_selectors_service.bad.yml", diff --git a/config/testdata/kubernetes_selectors_pod.bad.yml b/config/testdata/kubernetes_selectors_pod.bad.yml index 3a1a83abd2..24285c0ce0 100644 --- a/config/testdata/kubernetes_selectors_pod.bad.yml +++ b/config/testdata/kubernetes_selectors_pod.bad.yml @@ -6,3 +6,6 @@ scrape_configs: - role: "node" label: "foo=bar" field: "metadata.status=Running" + - role: "service" + label: "baz=que" + field: "metadata.status=Running" diff --git a/config/testdata/kubernetes_selectors_pod.good.yml b/config/testdata/kubernetes_selectors_pod.good.yml index 91da6ada17..49c17d72ce 100644 --- a/config/testdata/kubernetes_selectors_pod.good.yml +++ b/config/testdata/kubernetes_selectors_pod.good.yml @@ -11,3 +11,8 @@ scrape_configs: - role: "pod" label: "foo in (bar,baz)" field: "metadata.status=Running" + - role: pod + selectors: + - role: "node" + label: "foo=bar" + field: "metadata.status=Running" diff --git a/discovery/kubernetes/kubernetes.go b/discovery/kubernetes/kubernetes.go index 678f287ef5..6bbbafe8ea 100644 --- a/discovery/kubernetes/kubernetes.go +++ b/discovery/kubernetes/kubernetes.go @@ -194,7 +194,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error { foundSelectorRoles := make(map[Role]struct{}) allowedSelectors := map[Role][]string{ - RolePod: {string(RolePod)}, + RolePod: {string(RolePod), string(RoleNode)}, RoleService: {string(RoleService)}, RoleEndpointSlice: {string(RolePod), string(RoleService), string(RoleEndpointSlice)}, RoleEndpoint: {string(RolePod), string(RoleService), string(RoleEndpoint)}, diff --git a/discovery/kubernetes/pod.go b/discovery/kubernetes/pod.go index 1fed78b3a7..05b778bb59 100644 --- a/discovery/kubernetes/pod.go +++ b/discovery/kubernetes/pod.go @@ -286,6 +286,19 @@ func (p *Pod) buildPod(pod *apiv1.Pod) *targetgroup.Group { return tg } + // Filter out pods scheduled on nodes that are not in the node store, as + // these were filtered out by node selectors. + if p.withNodeMetadata { + _, exists, err := p.nodeInf.GetStore().GetByKey(pod.Spec.NodeName) + if err != nil { + p.logger.Error("failed to get node from store", "node", pod.Spec.NodeName, "err", err) + return tg + } + if !exists { + return tg + } + } + tg.Labels = podLabels(pod) tg.Labels[namespaceLabel] = lv(pod.Namespace) if p.withNodeMetadata { diff --git a/discovery/kubernetes/pod_test.go b/discovery/kubernetes/pod_test.go index db5db546d0..df8f42fcd3 100644 --- a/discovery/kubernetes/pod_test.go +++ b/discovery/kubernetes/pod_test.go @@ -627,3 +627,74 @@ func TestPodDiscoveryWithUpdatedNamespaceMetadata(t *testing.T) { }, }.Run(t) } + +func TestPodDiscoveryWithNodeSelector(t *testing.T) { + t.Parallel() + + workerNode := makeNode("worker-node", "10.0.0.1", "", map[string]string{"node-type": "worker"}, nil) + filteredNode := makeNode("filtered-node", "10.0.0.2", "", map[string]string{"node-type": "master"}, nil) + + attachMetadata := AttachMetadataConfig{ + Node: true, // necessary for node role selectos to work for pod role + } + n, c := makeDiscoveryWithMetadata(RolePod, NamespaceDiscovery{}, attachMetadata, workerNode, filteredNode) + n.selectors = roleSelector{ + node: resourceSelector{ + label: "node-type=worker", + }, + } + + podOnWorker := makePods("default") + podOnWorker.Name = "pod-on-worker" + podOnWorker.UID = types.UID("worker-pod-123") + podOnWorker.Spec.NodeName = "worker-node" + podOnWorker.Status.PodIP = "192.168.1.1" + + podOnFilteredNode := makePods("default") + podOnFilteredNode.Name = "pod-on-filtered-node" + podOnFilteredNode.UID = types.UID("filtered-pod-456") + podOnFilteredNode.Spec.NodeName = "filtered-node" + podOnFilteredNode.Status.PodIP = "192.168.1.2" + + k8sDiscoveryTest{ + discovery: n, + beforeRun: func() { + c.CoreV1().Pods("default").Create(context.Background(), podOnWorker, metav1.CreateOptions{}) + c.CoreV1().Pods("default").Create(context.Background(), podOnFilteredNode, metav1.CreateOptions{}) + }, + expectedMaxItems: 2, + expectedRes: map[string]*targetgroup.Group{ + "pod/default/pod-on-worker": { + Targets: []model.LabelSet{ + { + "__address__": "192.168.1.1:9000", + "__meta_kubernetes_pod_container_image": "testcontainer:latest", + "__meta_kubernetes_pod_container_name": "testcontainer", + "__meta_kubernetes_pod_container_port_name": "testport", + "__meta_kubernetes_pod_container_port_number": "9000", + "__meta_kubernetes_pod_container_port_protocol": "TCP", + "__meta_kubernetes_pod_container_init": "false", + "__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6", + }, + }, + Labels: model.LabelSet{ + "__meta_kubernetes_namespace": "default", + "__meta_kubernetes_pod_name": "pod-on-worker", + "__meta_kubernetes_pod_ip": "192.168.1.1", + "__meta_kubernetes_pod_ready": "true", + "__meta_kubernetes_pod_phase": "Running", + "__meta_kubernetes_pod_node_name": "worker-node", + "__meta_kubernetes_pod_host_ip": "2.3.4.5", + "__meta_kubernetes_pod_uid": "worker-pod-123", + "__meta_kubernetes_node_name": "worker-node", + "__meta_kubernetes_node_label_node_type": "worker", + "__meta_kubernetes_node_labelpresent_node_type": "true", + }, + Source: "pod/default/pod-on-worker", + }, + "pod/default/pod-on-filtered-node": { + Source: "pod/default/pod-on-filtered-node", + }, + }, + }.Run(t) +} diff --git a/discovery/manager.go b/discovery/manager.go index 3f2b2db652..9994e0bf74 100644 --- a/discovery/manager.go +++ b/discovery/manager.go @@ -475,6 +475,7 @@ func (m *Manager) allGroups() map[string][]*targetgroup.Group { for setName, v := range n { m.metrics.DiscoveredTargets.WithLabelValues(setName).Set(float64(v)) + m.metrics.LastUpdated.WithLabelValues(setName).SetToCurrentTime() } return tSets diff --git a/discovery/manager_test.go b/discovery/manager_test.go index 8a49005100..f21ab2574a 100644 --- a/discovery/manager_test.go +++ b/discovery/manager_test.go @@ -1595,3 +1595,30 @@ func TestConfigReloadAndShutdownRace(t *testing.T) { cancel() wgBg.Wait() } + +func TestGaugeLastUpdateTimestamp(t *testing.T) { + ctx := t.Context() + + reg := prometheus.NewRegistry() + _, sdMetrics := NewTestMetrics(t, reg) + + discoveryManager := NewManager(ctx, promslog.NewNopLogger(), reg, sdMetrics) + require.NotNil(t, discoveryManager) + discoveryManager.updatert = 100 * time.Millisecond + go discoveryManager.Run() + + c := map[string]Configs{ + "prometheus": { + staticConfig("foo:9090"), + }, + } + discoveryManager.ApplyConfig(c) + + before := time.Now() + <-discoveryManager.SyncCh() + after := time.Now() + + ts := client_testutil.ToFloat64(discoveryManager.metrics.LastUpdated.WithLabelValues("prometheus")) + require.GreaterOrEqual(t, ts, float64(before.UnixNano())/1e9, "last update timestamp should be >= time before sync") + require.LessOrEqual(t, ts, float64(after.UnixNano())/1e9, "last update timestamp should be <= time after sync") +} diff --git a/discovery/metrics.go b/discovery/metrics.go index 2a3734fb2d..3eefd3d0bb 100644 --- a/discovery/metrics.go +++ b/discovery/metrics.go @@ -26,6 +26,7 @@ type Metrics struct { ReceivedUpdates prometheus.Counter DelayedUpdates prometheus.Counter SentUpdates prometheus.Counter + LastUpdated *prometheus.GaugeVec } func NewManagerMetrics(registerer prometheus.Registerer, sdManagerName string) (*Metrics, error) { @@ -72,12 +73,22 @@ func NewManagerMetrics(registerer prometheus.Registerer, sdManagerName string) ( }, ) + m.LastUpdated = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "prometheus_sd_last_update_timestamp_seconds", + Help: "Timestamp of the last update sent to the SD consumers.", + ConstLabels: prometheus.Labels{"name": sdManagerName}, + }, + []string{"config"}, + ) + metrics := []prometheus.Collector{ m.FailedConfigs, m.DiscoveredTargets, m.ReceivedUpdates, m.DelayedUpdates, m.SentUpdates, + m.LastUpdated, } for _, collector := range metrics { @@ -97,4 +108,5 @@ func (m *Metrics) Unregister(registerer prometheus.Registerer) { registerer.Unregister(m.ReceivedUpdates) registerer.Unregister(m.DelayedUpdates) registerer.Unregister(m.SentUpdates) + registerer.Unregister(m.LastUpdated) } diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 060319ae92..1525269441 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -3746,3 +3746,6 @@ headers: tls_config: [ ] ``` + +If query logging and tracing are both enabled, a traceID and spanID will be injected +into the query log file for use in log/trace correlation. diff --git a/documentation/examples/remote_storage/go.mod b/documentation/examples/remote_storage/go.mod index 0c80c6e7c6..7f1b581598 100644 --- a/documentation/examples/remote_storage/go.mod +++ b/documentation/examples/remote_storage/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/prometheus/documentation/examples/remote_storage -go 1.25.5 +go 1.25.0 require ( github.com/alecthomas/kingpin/v2 v2.4.0 @@ -64,7 +64,7 @@ require ( github.com/googleapis/gax-go/v2 v2.17.0 // indirect github.com/gophercloud/gophercloud/v2 v2.10.0 // indirect github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 // indirect - github.com/hashicorp/consul/api v1.33.2 // indirect + github.com/hashicorp/consul/api v1.32.1 // indirect github.com/hashicorp/go-version v1.8.0 // indirect github.com/hashicorp/nomad/api v0.0.0-20260209224925-94b77491c895 // indirect github.com/hetznercloud/hcloud-go/v2 v2.36.0 // indirect diff --git a/documentation/examples/remote_storage/go.sum b/documentation/examples/remote_storage/go.sum index 6ebede1adf..c8909ca671 100644 --- a/documentation/examples/remote_storage/go.sum +++ b/documentation/examples/remote_storage/go.sum @@ -184,8 +184,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 h1:cLN4IBkmkYZNnk7EAJ0BHIethd+J6LqxFNw5mSiI2bM= github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/hashicorp/consul/api v1.33.2 h1:Q6mE0WZsUTJerlnl9TuXzqrtZ0cKdOCsxcZhj5mKbMs= -github.com/hashicorp/consul/api v1.33.2/go.mod h1:K3yoL/vnIBcQV/25NeMZVokRvPPERiqp2Udtr4xAfhs= +github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= +github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= github.com/hashicorp/cronexpr v1.1.3 h1:rl5IkxXN2m681EfivTlccqIryzYJSXRGRNa0xeG7NA4= github.com/hashicorp/cronexpr v1.1.3/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= @@ -255,6 +255,8 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= diff --git a/go.mod b/go.mod index 803cf2b926..860ac1bea4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/prometheus -go 1.25.5 +go 1.25.0 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 @@ -41,7 +41,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gophercloud/gophercloud/v2 v2.10.0 github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 - github.com/hashicorp/consul/api v1.33.2 + github.com/hashicorp/consul/api v1.32.1 github.com/hashicorp/nomad/api v0.0.0-20260220212019-daca79db0bd6 github.com/hetznercloud/hcloud-go/v2 v2.36.0 github.com/ionos-cloud/sdk-go/v6 v6.3.6 @@ -57,8 +57,8 @@ require ( github.com/oklog/ulid/v2 v2.1.1 github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.145.0 github.com/ovh/go-ovh v1.9.0 - github.com/pb33f/libopenapi v0.33.4 - github.com/pb33f/libopenapi-validator v0.11.1 + github.com/pb33f/libopenapi v0.34.0 + github.com/pb33f/libopenapi-validator v0.13.0 github.com/prometheus/alertmanager v0.31.1 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_golang/exp v0.0.0-20260108101519-fb0838f53562 @@ -122,7 +122,8 @@ require ( github.com/go-openapi/swag/stringutils v0.25.4 // indirect github.com/go-openapi/swag/typeutils v0.25.4 // indirect github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/pb33f/jsonpath v0.7.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pb33f/jsonpath v0.8.1 // indirect github.com/pb33f/ordered-map/v2 v2.3.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sirupsen/logrus v1.9.4 // indirect @@ -161,7 +162,7 @@ require ( github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect diff --git a/go.sum b/go.sum index e288929bea..74b1217d90 100644 --- a/go.sum +++ b/go.sum @@ -156,8 +156,8 @@ github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -280,10 +280,10 @@ github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 h1:cLN4IBkmkYZNnk7E github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= -github.com/hashicorp/consul/api v1.33.2 h1:Q6mE0WZsUTJerlnl9TuXzqrtZ0cKdOCsxcZhj5mKbMs= -github.com/hashicorp/consul/api v1.33.2/go.mod h1:K3yoL/vnIBcQV/25NeMZVokRvPPERiqp2Udtr4xAfhs= -github.com/hashicorp/consul/sdk v0.17.1 h1:LumAh8larSXmXw2wvw/lK5ZALkJ2wK8VRwWMLVV5M5c= -github.com/hashicorp/consul/sdk v0.17.1/go.mod h1:EngiixMhmw9T7wApycq6rDRFXXVUwjjf7HuLiGMH/Sw= +github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= +github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= +github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= +github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= github.com/hashicorp/cronexpr v1.1.3 h1:rl5IkxXN2m681EfivTlccqIryzYJSXRGRNa0xeG7NA4= github.com/hashicorp/cronexpr v1.1.3/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -412,6 +412,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -465,12 +467,12 @@ github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pb33f/jsonpath v0.7.1 h1:dEp6oIZuJbpDSyuHAl9m7GonoDW4M20BcD5vT0tPYRE= -github.com/pb33f/jsonpath v0.7.1/go.mod h1:zBV5LJW4OQOPatmQE2QdKpGQJvhDTlE5IEj6ASaRNTo= -github.com/pb33f/libopenapi v0.33.4 h1:Rgczgrg4VQKXW/NtSj/nApmtYKS+TVpLgTsG692JxmE= -github.com/pb33f/libopenapi v0.33.4/go.mod h1:e/dmd2Pf1nkjqkI0r7guFSyt9T5V0IIQKgs0L6B/3b0= -github.com/pb33f/libopenapi-validator v0.11.1 h1:lTW738oB3lwpS9poDzmI3jpTPZSb5W46vklZqtyf7+Q= -github.com/pb33f/libopenapi-validator v0.11.1/go.mod h1:7CfboslU/utKhiuQRuenriGYZ+HQLDOvARxjqRwd57w= +github.com/pb33f/jsonpath v0.8.1 h1:84C6QRyx6HcSm6PZnsMpcqYot3IsZ+m0n95+0NbBbvs= +github.com/pb33f/jsonpath v0.8.1/go.mod h1:zBV5LJW4OQOPatmQE2QdKpGQJvhDTlE5IEj6ASaRNTo= +github.com/pb33f/libopenapi v0.34.0 h1:jY8pf4yBHRObnNBrjuVDhVpgKjSUE8hLFpeoYtyQ/eo= +github.com/pb33f/libopenapi v0.34.0/go.mod h1:YOP20KzYe3mhE5301aQzJtzQ9MnvhABBGO7RMttA4V4= +github.com/pb33f/libopenapi-validator v0.13.0 h1:an3BxwklmLF4bxacudLV8Vysvw1krlAjpYoUfyJUgw8= +github.com/pb33f/libopenapi-validator v0.13.0/go.mod h1:YZQRDh+8xap/H0GM0cJsBrqqT+XLlMivA/qwqRLiidQ= github.com/pb33f/ordered-map/v2 v2.3.0 h1:k2OhVEQkhTCQMhAicQ3Z6iInzoZNQ7L9MVomwKBZ5WQ= github.com/pb33f/ordered-map/v2 v2.3.0/go.mod h1:oe5ue+6ZNhy7QN9cPZvPA23Hx0vMHnNVeMg4fGdCANw= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= diff --git a/go.work b/go.work index aea341baab..e940db9214 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.25.5 +go 1.25.0 use ( . diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 041724c22d..216e73c62b 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/prometheus/internal/tools -go 1.25.5 +go 1.25.0 require ( github.com/bufbuild/buf v1.65.0 diff --git a/promql/engine.go b/promql/engine.go index 970edcf78f..cbbc27a2de 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -700,7 +700,11 @@ func (ng *Engine) exec(ctx context.Context, q *query) (v parser.Value, ws annota } f = append(f, slog.Any("stats", stats.NewQueryStats(q.Stats()))) if span := trace.SpanFromContext(ctx); span != nil { - f = append(f, slog.Any("spanID", span.SpanContext().SpanID())) + spanCtx := span.SpanContext() + f = append(f, + slog.Any("spanID", spanCtx.SpanID()), + slog.Any("traceID", spanCtx.TraceID()), + ) } if origin := ctx.Value(QueryOrigin{}); origin != nil { for k, v := range origin.(map[string]any) { @@ -1961,9 +1965,7 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value, // Matrix evaluation always returns the evaluation time, // so this function needs special handling when given // a vector selector. - arg := unwrapStepInvariantExpr(e.Args[0]) - vs, ok := arg.(*parser.VectorSelector) - if ok { + if vs, ok := e.Args[0].(*parser.VectorSelector); ok { return ev.rangeEvalTimestampFunctionOverVectorSelector(ctx, vs, call, e) } } @@ -4215,13 +4217,6 @@ func unwrapParenExpr(e *parser.Expr) { } } -func unwrapStepInvariantExpr(e parser.Expr) parser.Expr { - if p, ok := e.(*parser.StepInvariantExpr); ok { - return p.Expr - } - return e -} - // PreprocessExpr wraps all possible step invariant parts of the given expression with // StepInvariantExpr. It also resolves the preprocessors, evaluates duration expressions // into their numeric values and removes superfluous parenthesis on parameters to functions and aggregations. @@ -4280,15 +4275,24 @@ func preprocessExprHelper(expr parser.Expr, start, end time.Time) (isStepInvaria case *parser.Call: _, ok := AtModifierUnsafeFunctions[n.Func.Name] isStepInvariant := !ok + // A special case to allow timestamp() to be wrapped in a step invariant. + // timestamp() is considered AtModifierUnsafe, but it can be safe depending on its arguments. + // ie timestamp(metric @ 1) is step invariant, but timestamp(abs(metric @ 1)) is not. + isTimestampWithAllArgsStepInvariantSafe := n.Func.Name == "timestamp" shouldWrap := make([]bool, len(n.Args)) for i := range n.Args { unwrapParenExpr(&n.Args[i]) var argIsStepInvariant bool argIsStepInvariant, shouldWrap[i] = preprocessExprHelper(n.Args[i], start, end) isStepInvariant = isStepInvariant && argIsStepInvariant + + _, argIsVectorSelector := n.Args[i].(*parser.VectorSelector) + if !argIsStepInvariant || !argIsVectorSelector { + isTimestampWithAllArgsStepInvariantSafe = false + } } - if isStepInvariant { + if isStepInvariant || isTimestampWithAllArgsStepInvariantSafe { // The function and all arguments are step invariant. return true, true } diff --git a/promql/engine_test.go b/promql/engine_test.go index 5dfffd7cc7..739818d7c1 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -3252,6 +3252,59 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { }, }, }, + { + input: "timestamp(metric @ 10)", + expected: &parser.StepInvariantExpr{ + Expr: &parser.Call{ + Func: parser.MustGetFunction("timestamp"), + Args: parser.Expressions{ + &parser.VectorSelector{ + Name: "metric", + Timestamp: makeInt64Pointer(10000), + LabelMatchers: []*labels.Matcher{ + parser.MustLabelMatcher(labels.MatchEqual, "__name__", "metric"), + }, + PosRange: posrange.PositionRange{ + Start: 10, + End: 21, + }, + }, + }, + PosRange: posrange.PositionRange{Start: 0, End: 22}, + }, + }, + }, + { + input: "timestamp(abs(metric @ 10))", + expected: &parser.Call{ + Func: parser.MustGetFunction("timestamp"), + Args: parser.Expressions{ + &parser.StepInvariantExpr{ + Expr: &parser.Call{ + Func: parser.MustGetFunction("abs"), + Args: parser.Expressions{ + &parser.VectorSelector{ + Name: "metric", + Timestamp: makeInt64Pointer(10000), + LabelMatchers: []*labels.Matcher{ + parser.MustLabelMatcher(labels.MatchEqual, "__name__", "metric"), + }, + PosRange: posrange.PositionRange{ + Start: 14, + End: 25, + }, + }, + }, + PosRange: posrange.PositionRange{ + Start: 10, + End: 26, + }, + }, + }, + }, + PosRange: posrange.PositionRange{Start: 0, End: 27}, + }, + }, } for _, test := range testCases { diff --git a/promql/promqltest/testdata/at_modifier.test b/promql/promqltest/testdata/at_modifier.test index 194c877803..65f102d514 100644 --- a/promql/promqltest/testdata/at_modifier.test +++ b/promql/promqltest/testdata/at_modifier.test @@ -255,3 +255,56 @@ eval instant at 1111111s stdvar_over_time({__name__="up"}[1h:1m] @ 1111111) eval instant at 1111111s mad_over_time({__name__="up"}[1h:1m] @ 1111111) clear + +# Additional tests specific to timestamp() and @ modifier usage. +load 10s + metric 0+1x10 + metric_missing 0 _ 2 _ 4 _ 5 _ 6 + +# Return a vector where each sample is set to the metric value at T=11. +# Since T=11 falls within the [10s,20s) scrap window the sample value at T=10s is returned. +eval range from 0 to 60s step 10s metric @ 11 + {__name__="metric"} 1 1 1 1 1 1 1 + +eval range from 0 to 60s step 10s abs(metric @ 11) + {} 1 1 1 1 1 1 1 + +# Return a vector where each sample's value is set to the timestamp of each sample in the given metric series +eval range from 0 to 60s step 10s timestamp(metric) + {} 0 10 20 30 40 50 60 + +# Return a vector where each sample's value is set to the timestamp for the metric's sample used at T=11s. +# The result is 10 since the metric at T=11s falls within the [10s,20s) scrape window. +# The result is the timestamp of the sample at T=10s +eval range from 0 to 60s step 10s timestamp(metric @ 11) + {} 10 10 10 10 10 10 10 + +# As above - illustrating the sample used at the upper end of the [10s,20s) scrape window. +eval range from 0 to 60s step 10s timestamp(metric @ 19) + {} 10 10 10 10 10 10 10 + +# As above - illustrating the transition to the next scrap window. +eval range from 0 to 60s step 10s timestamp(metric @ 20) + {} 20 20 20 20 20 20 20 + +eval range from 0 to 60s step 10s timestamp(metric_missing @ 0) + {} 0 0 0 0 0 0 0 + +# The timestamp of 0 is returned since the sample is missing from the [10s,20s) scrape window. +# As such, the previous sample from T=0s is returned. +eval range from 0 to 60s step 10s timestamp(metric_missing @ 10) + {} 0 0 0 0 0 0 0 + +eval range from 0 to 60s step 10s timestamp(metric_missing @ 20) + {} 20 20 20 20 20 20 20 + +# The timestamps for each step are returned since abs() returns a new vector with new [T,V] samples. +# Each sample in this vector has its value set to the absolute value of the sample value at T=10s, and its timestamp aligned to the step interval. +# This is unlike the above tests where timestamp() is operating on a vector with the original series samples. +eval range from 0 to 60s step 10s timestamp(abs(metric @ 11)) + {} 0 10 20 30 40 50 60 + +eval range from 0 to 60s step 10s timestamp(abs(metric_missing @ 11)) + {} 0 10 20 30 40 50 60 + +clear \ No newline at end of file diff --git a/tsdb/db_test.go b/tsdb/db_test.go index bb3044ccf4..fe2a3eaa65 100644 --- a/tsdb/db_test.go +++ b/tsdb/db_test.go @@ -8331,23 +8331,28 @@ func testDiskFillingUpAfterDisablingOOO(t *testing.T, scenario sampleTypeScenari opts := DefaultOptions() opts.OutOfOrderTimeWindow = 60 * time.Minute.Milliseconds() + // Use lower SamplesPerChunk and OutOfOrderCapMax so we need fewer samples + // to fill chunks, reducing the overall test time significantly + // (important for slow CI like i386 which can be 60x+ slower). + opts.SamplesPerChunk = 15 + opts.OutOfOrderCapMax = 5 db := newTestDB(t, withOpts(opts)) db.DisableCompactions() var ( - ctx = t.Context() - series1 = labels.FromStrings("foo", "bar1") - allSamples []chunks.Sample + ctx = t.Context() + series1 = labels.FromStrings("foo", "bar1") ) + // Use step of 5 minutes to reduce sample count while preserving time ranges + // needed for compaction triggers. This reduces total samples from ~411 to ~83. addSamples := func(fromMins, toMins int64) { app := appenderFn(db, ctx) - for m := fromMins; m <= toMins; m++ { + for m := fromMins; m <= toMins; m += 5 { ts := m * time.Minute.Milliseconds() - _, s, err := scenario.appendFunc(app, series1, ts, ts) + _, _, err := scenario.appendFunc(app, series1, ts, ts) require.NoError(t, err) - allSamples = append(allSamples, s) } require.NoError(t, app.Commit()) } diff --git a/tsdb/head.go b/tsdb/head.go index 917bd666d3..5e8aec3e6d 100644 --- a/tsdb/head.go +++ b/tsdb/head.go @@ -2161,15 +2161,24 @@ func (h *Head) deleteSeriesByID(refs []chunks.HeadSeriesRef) { ) for _, ref := range refs { + // Delete the reference from the series map. + // Copying getByID here to avoid locking and unlocking twice. refShard := int(ref) & (h.series.size - 1) h.series.locks[refShard].Lock() - - // Copying getByID here to avoid locking and unlocking twice. series := h.series.series[refShard][ref] if series == nil { h.series.locks[refShard].Unlock() continue } + delete(h.series.series[refShard], series.ref) + h.series.locks[refShard].Unlock() + + // Delete the reference from the hash. + hash := series.lset.Hash() + hashShard := int(hash) & (h.series.size - 1) + h.series.locks[hashShard].Lock() + h.series.hashes[hashShard].del(hash, series.ref) + h.series.locks[hashShard].Unlock() if value.IsStaleNaN(series.lastValue) || (series.lastHistogramValue != nil && value.IsStaleNaN(series.lastHistogramValue.Sum)) || @@ -2177,9 +2186,6 @@ func (h *Head) deleteSeriesByID(refs []chunks.HeadSeriesRef) { staleSeriesDeleted++ } - hash := series.lset.Hash() - hashShard := int(hash) & (h.series.size - 1) - chunksRemoved += len(series.mmappedChunks) if series.headChunks != nil { chunksRemoved += series.headChunks.len() @@ -2187,10 +2193,6 @@ func (h *Head) deleteSeriesByID(refs []chunks.HeadSeriesRef) { deleted[storage.SeriesRef(series.ref)] = struct{}{} series.lset.Range(func(l labels.Label) { affected[l] = struct{}{} }) - h.series.hashes[hashShard].del(hash, series.ref) - delete(h.series.series[refShard], series.ref) - - h.series.locks[refShard].Unlock() } h.metrics.seriesRemoved.Add(float64(len(deleted))) diff --git a/tsdb/head_test.go b/tsdb/head_test.go index c1326893ef..7f0af8b41f 100644 --- a/tsdb/head_test.go +++ b/tsdb/head_test.go @@ -7705,3 +7705,77 @@ func TestHeadAppender_STStorage_ChunkEncoding(t *testing.T) { }) } } + +// TestWALReplayRaceWithStaleSeriesCompaction verifies that deleteSeriesByID correctly locks the +// hash shard (not only the ref shard) when deleting from the hashes map. +// The race only occurs when Prometheus restarts after having done a stale series compaction because +// deleteSeriesByID is not used otherwise. +func TestWALReplayRaceWithStaleSeriesCompaction(t *testing.T) { + opts := newTestHeadDefaultOptions(1000, false) + // A small stripe size ensures many series share hash shards, increasing + // the likelihood that deleteSeriesByID and getOrCreateWithOptionalID + // contend on the same shard during WAL replay. + opts.StripeSize = 32 + head, _ := newTestHeadWithOptions(t, compression.None, opts) + require.NoError(t, head.Init(0)) + + appendSample := func(lbls labels.Labels, ts int64, val float64) { + app := head.Appender(context.Background()) + _, err := app.Append(0, lbls, ts, val) + require.NoError(t, err) + require.NoError(t, app.Commit()) + } + + // Step 1: Create a batch of series and make them stale. + const numStaleSeries = 500 + staleLbls := make([]labels.Labels, numStaleSeries) + for i := range numStaleSeries { + staleLbls[i] = labels.FromStrings("__name__", "stale_metric", "i", strconv.Itoa(i)) + appendSample(staleLbls[i], 100, float64(i)) + } + for _, lbl := range staleLbls { + appendSample(lbl, 200, math.Float64frombits(value.StaleNaN)) + } + require.Equal(t, uint64(numStaleSeries), head.NumStaleSeries()) + + // Step 2: Truncate stale series. This removes them from the Head and + // writes tombstone records (with Mint=MinInt64, Maxt=MaxInt64) to the WAL. + staleRefs := make([]storage.SeriesRef, 0, numStaleSeries) + for i := range numStaleSeries { + ms := head.series.getByHash(staleLbls[i].Hash(), staleLbls[i]) + require.NotNil(t, ms) + staleRefs = append(staleRefs, storage.SeriesRef(ms.ref)) + } + require.NoError(t, head.truncateStaleSeries(staleRefs, 300)) + require.Equal(t, uint64(0), head.NumStaleSeries()) + require.Equal(t, uint64(0), head.NumSeries()) + + // Step 3: Add new series AFTER the truncation. In the WAL, these series + // records appear after the tombstone records. During replay, the main + // goroutine will create these series (via getOrCreateWithOptionalID, which + // accesses hashes[hashShard] under locks[hashShard]) concurrently with + // the walSubsetProcessor goroutines deleting the stale series (via + // deleteSeriesByID, which must also lock the correct hashShard). + const numNewSeries = 500 + for i := range numNewSeries { + lbl := labels.FromStrings("__name__", "new_metric", "i", strconv.Itoa(i)) + appendSample(lbl, 300, float64(i)) + } + require.Equal(t, uint64(numNewSeries), head.NumSeries()) + + // Step 4: Close and re-open the Head to trigger WAL replay. + // With the buggy locking, the race detector should catch the data race + // between the main goroutine (creating series) and worker goroutines + // (deleting stale series) during replay. + require.NoError(t, head.Close()) + + wal, err := wlog.NewSize(nil, nil, filepath.Join(head.opts.ChunkDirRoot, "wal"), 32768, compression.None) + require.NoError(t, err) + head, err = NewHead(nil, nil, wal, nil, head.opts, nil) + require.NoError(t, err) + require.NoError(t, head.Init(0)) // Should not cause a race here. + + require.Equal(t, uint64(0), head.NumStaleSeries()) + require.Equal(t, uint64(numNewSeries), head.NumSeries()) + require.NoError(t, head.Close()) +} diff --git a/util/testutil/synctest/disabled.go b/util/testutil/synctest/disabled.go deleted file mode 100644 index 595b93c650..0000000000 --- a/util/testutil/synctest/disabled.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright The Prometheus 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. - -//go:build !goexperiment.synctest && !go1.25 - -package synctest - -import ( - "testing" -) - -func Test(t *testing.T, _ func(t *testing.T)) { - t.Skip("goexperiment.synctest is not enabled") -} - -func Wait() { - // It isn't meant to be called outside of Test(). - panic("goexperiment.synctest is not enabled") -} diff --git a/util/testutil/synctest/enabled.go b/util/testutil/synctest/enabled.go deleted file mode 100644 index d219903809..0000000000 --- a/util/testutil/synctest/enabled.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright The Prometheus 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. - -//go:build goexperiment.synctest && !go1.25 - -package synctest - -import ( - "testing" - "testing/synctest" -) - -func Test(t *testing.T, f func(t *testing.T)) { - synctest.Run(func() { - f(t) - }) -} - -func Wait() { - synctest.Wait() -} diff --git a/util/testutil/synctest/synctest.go b/util/testutil/synctest/synctest.go index 41750f9892..e3099c2d6a 100644 --- a/util/testutil/synctest/synctest.go +++ b/util/testutil/synctest/synctest.go @@ -11,8 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build go1.25 - package synctest import ( diff --git a/web/ui/mantine-ui/package.json b/web/ui/mantine-ui/package.json index 8f35318090..fd57c6f088 100644 --- a/web/ui/mantine-ui/package.json +++ b/web/ui/mantine-ui/package.json @@ -1,7 +1,7 @@ { "name": "@prometheus-io/mantine-ui", "private": true, - "version": "0.309.1", + "version": "0.310.0", "type": "module", "scripts": { "start": "vite", @@ -28,7 +28,7 @@ "@microsoft/fetch-event-source": "^2.0.1", "@nexucis/fuzzy": "^0.5.1", "@nexucis/kvsearch": "^0.9.1", - "@prometheus-io/codemirror-promql": "0.309.1", + "@prometheus-io/codemirror-promql": "0.310.0", "@reduxjs/toolkit": "^2.11.2", "@tabler/icons-react": "^3.36.1", "@tanstack/react-query": "^5.90.20", diff --git a/web/ui/mantine-ui/src/promql/tools/go.mod b/web/ui/mantine-ui/src/promql/tools/go.mod index af604b1964..5030ae79db 100644 --- a/web/ui/mantine-ui/src/promql/tools/go.mod +++ b/web/ui/mantine-ui/src/promql/tools/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/prometheus/web/ui/mantine-ui/src/promql/tools -go 1.25.5 +go 1.25.0 require ( github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 diff --git a/web/ui/module/codemirror-promql/package.json b/web/ui/module/codemirror-promql/package.json index 5208513eab..cbc0589469 100644 --- a/web/ui/module/codemirror-promql/package.json +++ b/web/ui/module/codemirror-promql/package.json @@ -1,6 +1,6 @@ { "name": "@prometheus-io/codemirror-promql", - "version": "0.309.1", + "version": "0.310.0", "description": "a CodeMirror mode for the PromQL language", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", @@ -29,7 +29,7 @@ }, "homepage": "https://github.com/prometheus/prometheus/blob/main/web/ui/module/codemirror-promql/README.md", "dependencies": { - "@prometheus-io/lezer-promql": "0.309.1", + "@prometheus-io/lezer-promql": "0.310.0", "lru-cache": "^11.2.5" }, "devDependencies": { diff --git a/web/ui/module/lezer-promql/package.json b/web/ui/module/lezer-promql/package.json index 7a969b57e4..5666e0bbd2 100644 --- a/web/ui/module/lezer-promql/package.json +++ b/web/ui/module/lezer-promql/package.json @@ -1,6 +1,6 @@ { "name": "@prometheus-io/lezer-promql", - "version": "0.309.1", + "version": "0.310.0", "description": "lezer-based PromQL grammar", "main": "dist/index.cjs", "type": "module", diff --git a/web/ui/package-lock.json b/web/ui/package-lock.json index 7669399b66..a571c48405 100644 --- a/web/ui/package-lock.json +++ b/web/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "prometheus-io", - "version": "0.309.1", + "version": "0.310.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "prometheus-io", - "version": "0.309.1", + "version": "0.310.0", "workspaces": [ "mantine-ui", "module/*" @@ -24,7 +24,7 @@ }, "mantine-ui": { "name": "@prometheus-io/mantine-ui", - "version": "0.309.1", + "version": "0.310.0", "dependencies": { "@codemirror/autocomplete": "^6.20.0", "@codemirror/language": "^6.12.1", @@ -42,7 +42,7 @@ "@microsoft/fetch-event-source": "^2.0.1", "@nexucis/fuzzy": "^0.5.1", "@nexucis/kvsearch": "^0.9.1", - "@prometheus-io/codemirror-promql": "0.309.1", + "@prometheus-io/codemirror-promql": "0.310.0", "@reduxjs/toolkit": "^2.11.2", "@tabler/icons-react": "^3.36.1", "@tanstack/react-query": "^5.90.20", @@ -172,10 +172,10 @@ }, "module/codemirror-promql": { "name": "@prometheus-io/codemirror-promql", - "version": "0.309.1", + "version": "0.310.0", "license": "Apache-2.0", "dependencies": { - "@prometheus-io/lezer-promql": "0.309.1", + "@prometheus-io/lezer-promql": "0.310.0", "lru-cache": "^11.2.5" }, "devDependencies": { @@ -205,7 +205,7 @@ }, "module/lezer-promql": { "name": "@prometheus-io/lezer-promql", - "version": "0.309.1", + "version": "0.310.0", "license": "Apache-2.0", "devDependencies": { "@lezer/generator": "^1.8.0", diff --git a/web/ui/package.json b/web/ui/package.json index 172e646aeb..5b71a58550 100644 --- a/web/ui/package.json +++ b/web/ui/package.json @@ -1,7 +1,7 @@ { "name": "prometheus-io", "description": "Monorepo for the Prometheus UI", - "version": "0.309.1", + "version": "0.310.0", "private": true, "scripts": { "build": "bash build_ui.sh --all",