Visitar URL original
feat: add support for percentage-based retention by somaz94 · Pull Request #8728 · prometheus-operator/prometheus-operator · GitHub
Skip to content

feat: add support for percentage-based retention - #8728

Merged
simonpasquier merged 4 commits into
prometheus-operator:mainfrom
somaz94:feat/retention-percentage
Sep 3, 2026
Merged

simonpasquier merged 4 commits into
prometheus-operator:mainfrom
somaz94:feat/retention-percentage

Conversation

@somaz94

@somaz94 somaz94 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

Prometheus v3.11.0 exposes storage.tsdb.retention.percentage in the configuration
file only, so there's no command-line flag to pass through additionalArgs. This adds
spec.retentionPercentage and emits it next to the existing time and size settings.

Setting only the percentage no longer falls back to the default 24h time-based
retention, which would otherwise silently override the user's intent. The shard
retention grace period treats percentage-only the same as size-only. Prometheus
< v3.11.0 ignores the field and logs a warning, keeping its current behaviour.

Closes: #8516

Type of change

  • FEATURE (non-breaking change which adds functionality)

Verification

make test-unit, make check, make check-api, make check-license and
make test-prometheus-goldenfiles all pass locally. New cases cover config
generation (percentage only / with time+size / zero), the < v3.11.0 flag path,
the shard retention grace period, and out-of-range validation.

Changelog entry

Add `spec.retentionPercentage` field to the Prometheus CRD for configuring `storage.tsdb.retention.percentage` (requires Prometheus >= v3.11.0).

Assisted by Claude Code; I reviewed the change and ran the checks above myself.

Prometheus v3.11.0 exposes storage.tsdb.retention.percentage in the
configuration file only, so there is no command-line flag the operator
could pass through additionalArgs. Add spec.retentionPercentage and
emit it alongside the existing time and size retention settings.

Setting only the percentage no longer falls back to the default 24h
time-based retention, which would otherwise silently override the
user's intent. Older Prometheus versions ignore the field and keep
their current behaviour.

Closes prometheus-operator#8516

Assisted-by: Claude Code (Opus 5)
Signed-off-by: somaz <genius5711@gmail.com>
@somaz94
somaz94 marked this pull request as ready for review July 30, 2026 02:33
@somaz94
somaz94 requested a review from a team as a code owner July 30, 2026 02:33
// setting in the configuration file only. It requires Prometheus >= v3.11.0
// and is ignored by older versions.
// +optional
RetentionPercentage *resource.Quantity `json:"retentionPercentage,omitempty"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add CEL rule for the number between 0 and 100?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14fab58 — added a CEL rule on spec.retentionPercentage:

// +kubebuilder:validation:XValidation:rule="quantity(string(self)).compareTo(quantity('0')) >= 0 && quantity(string(self)).compareTo(quantity('100')) <= 0",message="retentionPercentage must be between 0 and 100"

The field is a *resource.Quantity, so it renders as x-kubernetes-int-or-string. string(self) covers both accepted forms (retentionPercentage: 80 and retentionPercentage: "0.5"), and quantity() gives correct numeric comparison for suffixed values instead of a lexical one.

I checked the compiled rule against apiextensions-apiserver's CEL validator (v0.36.3) rather than eyeballing it — accepted: 0, 80, 100, "0.5", "100m"; rejected: 101, -1, "100.1", "-0.1", "1Ki".

Two things worth your call:

  • The quantity CEL library was introduced in Kubernetes 1.28 (k8s.io/apiserver/pkg/cel/environment/base.go), so an apiserver older than that rejects the CRD at compile time. CI runs 1.36 (.github/env), and this is the first quantity() use in the repo — if you would rather keep the CRDs installable on older apiservers I can switch to a library-free formulation.
  • I kept the Go-side validateRetentionPercentage() as well, so the operator still returns a clear error where the rule cannot be evaluated.

Regenerated artifacts ride in the same commit (bundle.yaml, both CRD YAMLs, jsonnet); make --always-make format generate is a no-op on top of it, and make check-golang / make check-api are clean.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our documentation currently states that we support Kubernetes >= v1.25.0. I'm not opposed bumping the version requirement but it would need further discussion (1.25 is EOL since 2023-10-28).

https://prometheus-operator.dev/docs/getting-started/compatibility/#kubernetes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, and it is the same concern I flagged when I added the rule — quantity() landed in Kubernetes 1.28, so on 1.25–1.27 the apiserver rejects the CRD at compile time rather than just ignoring the rule.

For context on how it got here: the rule was added at @dongjiang1989's request in this thread, so the version floor is really a call between the two of you rather than something I should decide on my own.

Two ways out that do not need the floor bumped:

  • Drop the XValidation marker. validateRetentionPercentage() in pkg/prometheus/promcfg.go already rejects anything outside 0..100 and is covered by TestRetentionConfigFile (the 101 and -1 cases). The only thing lost is admission-time rejection — a bad value is caught at reconcile instead, with a clear error.
  • Keep the bound, express it without the quantity library. The field is a *resource.Quantity so it renders as x-kubernetes-int-or-string, and a library-free rule has to handle both the int and the string form. I can write and test that, but it will be noticeably uglier than the current one-liner.

Tell me which you prefer and I will push it. If you would rather take the 1.25 floor as a separate discussion, I am happy to just drop the marker for now so this PR is not blocked on it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the second option you're suggesting (Keep the bound, express it without the quantity library.). I'd rather remove the CEL rule for now and we can discuss later if/when we want to change the minimum supported Kubernetes version.

// retention defines how long to retain the Prometheus data.
//
// Default: "24h" if `spec.retention` and `spec.retentionSize` are empty.
// Default: "24h" if `spec.retention`, `spec.retentionSize` and

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add CEL rule for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay — your earlier comment on the same field slipped past me. Answered in the thread above: 14fab58 adds a quantity()-based CEL rule bounding retentionPercentage to 0..100, with the regenerated CRDs in the same commit.

Signed-off-by: somaz <genius5711@gmail.com>
// setting in the configuration file only. It requires Prometheus >= v3.11.0
// and is ignored by older versions.
// +optional
RetentionPercentage *resource.Quantity `json:"retentionPercentage,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our documentation currently states that we support Kubernetes >= v1.25.0. I'm not opposed bumping the version requirement but it would need further discussion (1.25 is EOL since 2023-10-28).

https://prometheus-operator.dev/docs/getting-started/compatibility/#kubernetes

Comment on lines +1238 to +1240
// Unlike `spec.retention` and `spec.retentionSize`, Prometheus exposes this
// setting in the configuration file only. It requires Prometheus >= v3.11.0
// and is ignored by older versions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the user doesn't need the implementation details.

Suggested change
// Unlike `spec.retention` and `spec.retentionSize`, Prometheus exposes this
// setting in the configuration file only. It requires Prometheus >= v3.11.0
// and is ignored by older versions.
// It requires Prometheus >= v3.11.0 and is ignored by older versions.

Comment thread pkg/prometheus/server/operator.go Outdated
Comment on lines +1346 to +1347
// If Prometheus is configured with size-based and/or percentage-based
// retention only, it returns a zero value.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If Prometheus is configured with size-based and/or percentage-based
// retention only, it returns a zero value.
// If Prometheus is configured with size-based and/or percentage-based
// retention with no time-based retention, it returns a zero value.

Comment thread pkg/prometheus/promcfg.go Outdated
}

if v := retentionPercentage.AsApproximateFloat64(); v < 0 || v > 100 {
return fmt.Errorf("`retentionPercentage` must be between 0 and 100. The current value is %s", retentionPercentage.String())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("`retentionPercentage` must be between 0 and 100. The current value is %s", retentionPercentage.String())
return fmt.Errorf("`retentionPercentage` must be between 0 and 100 (the current value is %q)", retentionPercentage.String())

Comment thread pkg/prometheus/promcfg.go Outdated
retentionSlice = append(retentionSlice, yaml.MapItem{Key: "size", Value: string(retentionSize)})
}
if retentionPercentage != nil {
retentionSlice = append(retentionSlice, yaml.MapItem{Key: "percentage", Value: retentionPercentage.AsApproximateFloat64()})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use cg.WithMinimumVersion("3.11.0").AppendMapItem(...) which would log a warning if the parameter isn't supported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That line sits inside the if cg.WithMinimumVersion("3.11.0").IsCompatible() branch, so AppendMapItem's notCompatible path cannot be reached from there — it would fall through to the plain append and never warn.

The warning you are describing is already emitted, from the else branch a few lines down:

} else if retentionPercentage != nil {
	// Percentage-based retention has no command-line equivalent, hence it
	// can't be supported by older Prometheus versions.
	cg.WithMinimumVersion("3.11.0").Warn("retentionPercentage")
}

TestRetentionConfigFile/retention.percentage is not in the configuration file for Prometheus < v3.11.0 covers it, and the line it logs is:

level=WARN msg="ignoring \"retentionPercentage\" not supported by Prometheus" version=v3.10.0 minimum_version=3.11.0

The whole storage.tsdb.retention section is gated as a unit because the config-file form only exists from 3.11.0 — time and size still go out as command-line flags below that — so the version check cannot move down to the individual key.

I tried your version to be sure: swapping in cg.WithMinimumVersion("3.11.0").AppendMapItem(retentionSlice, "percentage", ...) leaves every golden file unchanged. Happy to make that change anyway if you prefer it for consistency, but it would be a no-op.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would refactor the code to be like

var (
    retentionSlice yaml.MapSlice
    cgRetention = cg.WithMinimumVersion("3.11.0")
}
if cgRetention.IsCompatible() {
  // Starting with 3.11.0, time and size retention settings go to the configuration file instead of CLI arguments.
  ...
}
if retentionPercentage != nil {
    retentionSlice = cgRetention.AppendMapItem(retentionSlice, yaml.MapItem{Key: "percentage", Value: retentionPercentage.AsApproximateFloat64()})
}
if len(retentionSlice) > 0 {
    ...
}

Drop the configuration-file implementation detail from the field
documentation, clarify the gracePeriodForPrometheusStorage comment and
reword the validation error message, as suggested during review.

Generated artifacts are regenerated in the same commit.

Signed-off-by: somaz <genius5711@gmail.com>
@somaz94

somaz94 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Pushed 50bcc5670, which applies the three suggestions:

  • prometheus_types.go — dropped the configuration-file implementation detail from the field docs
  • pkg/prometheus/server/operator.go — reworded the gracePeriodForPrometheusStorage comment
  • pkg/prometheus/promcfg.go — reworded the validation error message

The first one propagates into six generated files, so the regenerated artifacts ride in the same commit — bundle.yaml, both example/** CRDs, the jsonnet CRD, Documentation/api-reference/api.md and pkg/client/applyconfiguration/monitoring/v1/prometheusspec.go. make --always-make format generate is a no-op on top of it.

I replied in the two remaining threads rather than pushing a change:

  • the AppendMapItem suggestion would be a no-op where it sits, and the warning it is meant to produce is already emitted from the else branch — details and the log line are in that thread
  • the CEL rule's Kubernetes floor is the one open question. The rule was added at @dongjiang1989's request, so I would rather you two settle whether to bump the documented 1.25 floor or drop the marker — I am happy to do either, and dropping it keeps this PR off that discussion.

… retention slice

The quantity() CEL library landed in Kubernetes 1.28, so the XValidation
rule rejected the CRD outright on 1.25-1.27 rather than being ignored.
Remove it instead of raising the minimum supported Kubernetes version;
validateRetentionPercentage() already rejects values outside 0..100.

Hoist the 3.11.0 ConfigGenerator into cgRetention and move the
percentage append out of the compatibility branch so AppendMapItem
emits the unsupported-version warning itself, instead of duplicating
that warning in an else branch.

Generated artifacts are regenerated in the same commit.

Signed-off-by: somaz <genius5711@gmail.com>
@somaz94

somaz94 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — pushed ea42a7496 with both.

CEL rule removed. Agreed on not moving the minimum Kubernetes version for this;
validateRetentionPercentage() already rejects anything outside 0..100, so dropping the
marker loses no validation. Regenerated CRDs ride in the same commit (bundle.yaml, both
example/**, the jsonnet CRD).

Retention slice restructured as you sketched — cgRetention hoisted, and the
retentionPercentage append moved out of the compatibility branch so AppendMapItem
emits the warning itself. Two notes on adapting it:

  • AppendMapItem's signature is (m yaml.MapSlice, k string, v any), so I passed
    "percentage" as the key rather than a yaml.MapItem. Side effect: the
    unsupported-version warning now reads ignoring "percentage" not supported by Prometheus
    where it used to say "retentionPercentage". Happy to keep the old wording via an
    explicit Warn() if you would rather the log name the spec field.
  • I guarded the tsdbSlice append with len(retentionSlice) > 0. It cannot actually be
    empty when compatible — RetentionTimeOrDefault falls back to the default whenever
    retention, size and percentage are all unset — so this is only a safety net, not a
    behavior change.

make --always-make format generate is a no-op on top, and go test ./pkg/... is green.

@simonpasquier simonpasquier left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@simonpasquier
simonpasquier merged commit 19f6d69 into prometheus-operator:main Sep 3, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for configuring storage.tsdb.retention.percentage

3 participants