Skip to content

K8SPG-786 - #1734

Open
nmarukovich wants to merge 14 commits into
mainfrom
K8SPG-786
Open

K8SPG-786#1734
nmarukovich wants to merge 14 commits into
mainfrom
K8SPG-786

Conversation

@nmarukovich

Copy link
Copy Markdown
Contributor

CHANGE DESCRIPTION

Problem:
The operator drops the pg_cron extension from the database and destroys all
jobs stored in cron.job. This happens to users who install pg_cron as a custom
extension from their own storage. It also happens when a user moves pg_cron
from the custom list to the builtin flags.

Cause:
pgCron and setUser were plain bools. If the user did not set them, the
operator set them to false. So the operator cannot see the difference between
"the user did not ask for pg_cron" and "the user wants pg_cron removed", and it
runs DROP EXTENSION IF EXISTS pg_cron.

The custom extensions loop drops an extension as soon as it is removed from the
custom list, even if the user enabled the same extension as a builtin one.
pg_cron**Solution:**PGCronandSetUserare*boolnow: nil means the flag is not set and the operator leaves the extension alone, false means drop it, true means create it. We do not setfalse` for these two flags anymore. With nil we also do not add
pg_cron to shared_preload_libraries, so postgres does not restart.

The custom extensions loop does not drop an extension if the same extension is
enabled as a builtin one. Now a user can move pg_cron from the custom list to
the builtin flags and keep the data.

new upgrade e2e tests
custom-extensions and upgrade-minor were extended.

CHECKLIST

Jira

  • Is the Jira ticket created and referenced properly?
  • Does the Jira ticket have the proper statuses for documentation (Needs Doc) and QA (Needs QA)?
  • Does the Jira ticket link to the proper milestone (Fix Version field)?

Tests

  • Is an E2E test/test case added for the new feature/change?
  • Are unit tests added where appropriate?

Config/Logging/Testability

  • Are all needed new/changed options added to default YAML files?
  • Are all needed new/changed options added to the Helm Chart?
  • Did we add proper logging messages for operator actions?
  • Did we ensure compatibility with the previous version or cluster upgrade process?
  • Does the change support oldest and newest supported PG version?
  • Does the change support oldest and newest supported Kubernetes version?

… into K8SPG-786

# Conflicts:
#	e2e-tests/functions
#	e2e-tests/run-pr.csv
#	e2e-tests/run-release.csv
#	e2e-tests/tests/custom-extensions/04-check-extensions.yaml
#	e2e-tests/tests/custom-extensions/08-check-extensions.yaml
#	e2e-tests/tests/custom-extensions/13-check-extensions.yaml
#	e2e-tests/tests/custom-extensions/16-check-extensions.yaml
#	pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go
#	pkg/apis/upstream.pgv2.percona.com/v1beta1/postgrescluster_types.go
#	pkg/apis/upstream.pgv2.percona.com/v1beta1/zz_generated.deepcopy.go
Comment thread e2e-tests/functions

case $test_name in
"custom-extensions" | "builtin-extensions" | major-upgrade* )
"custom-extensions" | "builtin-extensions" | "operator-upgrade-extensions" | major-upgrade* )

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.

[shfmt] reported by reviewdog 🐶

Suggested change
"custom-extensions" | "builtin-extensions" | "operator-upgrade-extensions" | major-upgrade* )
"custom-extensions" | "builtin-extensions" | "operator-upgrade-extensions" | major-upgrade*)

@nmarukovich nmarukovich changed the title K8 spg 786 K8SPG-786 Aug 9, 2026
Comment thread e2e-tests/functions
local gap
gap=$(run_psql_local 'SELECT COALESCE(ceil(EXTRACT(EPOCH FROM max(ts - prev)))::int,0) FROM (SELECT ts, lag(ts) OVER (ORDER BY ts) AS prev FROM heartbeat) g' "$uri" | tr -d '[:space:]')
echo "heartbeat max gap: ${gap}s (limit ${max_gap}s)"
if [[ -z "$gap" || "$gap" -gt "$max_gap" ]]; then

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.

[shfmt] reported by reviewdog 🐶

Suggested change
if [[ -z "$gap" || "$gap" -gt "$max_gap" ]]; then
if [[ -z $gap || $gap -gt $max_gap ]]; then

@JNKPercona

Copy link
Copy Markdown
Collaborator
Test Name Result Time
backup-enable-disable passed 00:00:00
builtin-extensions passed 00:00:00
custom-extensions passed 00:00:00
cert-manager-tls passed 00:00:00
cert-management-policy passed 00:00:00
custom-envs passed 00:00:00
custom-tls passed 00:00:00
database-init-sql passed 00:00:00
demand-backup passed 00:00:00
demand-backup-offline-snapshot passed 00:00:00
dynamic-configuration passed 00:00:00
extra-volumes passed 00:00:00
finalizers passed 00:00:00
init-deploy passed 00:00:00
huge-pages passed 00:00:00
major-upgrade-14-to-15 passed 00:00:00
major-upgrade-15-to-16 passed 00:00:00
major-upgrade-16-to-17 passed 00:00:00
major-upgrade-17-to-18 passed 00:00:00
ldap passed 00:00:00
ldap-tls passed 00:00:00
logcollection passed 00:00:00
monitoring passed 00:00:00
operator-upgrade-cert-manager passed 00:00:00
operator-upgrade-extensions passed 00:00:00
one-pod passed 00:00:00
repo-host-autogrow passed 00:00:00
operator-self-healing passed 00:00:00
pgbouncer passed 00:00:00
pg-tde passed 00:00:00
pg-tde-wal-encrypt passed 00:14:34
pitr passed 00:00:00
scaling passed 00:00:00
scheduled-backup passed 00:00:00
self-healing passed 00:00:00
sidecars passed 00:00:00
standby-pgbackrest passed 00:00:00
standby-streaming passed 00:00:00
start-from-backup passed 00:00:00
tablespaces passed 00:00:00
telemetry-transfer passed 00:00:00
upgrade-consistency passed 00:00:00
upgrade-minor passed 00:00:00
users passed 00:00:00
migration-from-crunchy-standby passed 00:00:00
migration-from-crunchy-pv passed 00:00:00
migration-from-crunchy-backup-restore passed 00:00:00
Summary Value
Tests Run 47/47
Job Duration 00:46:52
Total Test Time 00:14:34

commit: 2b0ca7a
image: perconalab/percona-postgresql-operator:PR-1734-2b0ca7a67

@nmarukovich

Copy link
Copy Markdown
Contributor Author

copilot

Copilot AI 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.

Pull request overview

This PR addresses K8SPG-786 by making pg_cron and set_user builtin extension management tri-state so the operator can distinguish between “unset/leave as-is” vs “explicitly disable/drop”, preventing unintended DROP EXTENSION (and data loss) during upgrades and when migrating extensions from spec.extensions.custom to builtin flags. It also expands E2E coverage to validate operator upgrades, minor upgrades, and cert-manager upgrade scenarios without triggering unwanted rollouts or data loss.

Changes:

  • Change upstream PostgresCluster extension flags for PGCron/SetUser from bool to *bool (nil = leave alone), and update deepcopy behavior.
  • Update reconciliation logic to respect nil vs false/true, and prevent custom-extension removal from dropping extensions that are now enabled via builtin flags.
  • Add/extend E2E tests for operator upgrades (extensions + cert-manager) and minor upgrade flow (backup/restore, rollout checks, log checks).

Reviewed changes

Copilot reviewed 70 out of 72 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/apis/upstream.pgv2.percona.com/v1beta1/zz_generated.deepcopy.go DeepCopy updates for new *bool extension fields.
pkg/apis/upstream.pgv2.percona.com/v1beta1/postgrescluster_types.go Convert PGCron/SetUser to tri-state *bool in upstream API.
pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go Stop defaulting PGCron/SetUser; map Percona CR -> upstream tri-state fields.
pkg/apis/pgv2.percona.com/v2/perconapgcluster_types_test.go Add unit tests covering tri-state passthrough + stored-spec clearing behavior.
percona/controller/pgcluster/controller.go Avoid dropping custom extensions when the same extension is enabled via builtin flags.
internal/controller/postgrescluster/postgres.go Treat nil builtin-extension flags as “do not touch”; keep explicit enable/disable behavior.
internal/controller/postgrescluster/controller.go Only add shared-preload params when tri-state flags are explicitly enabled.
e2e-tests/tests/upgrade-minor/conf/upgrade-minor.yaml New base config for richer upgrade-minor scenario (multi-repo, resources, etc.).
e2e-tests/tests/upgrade-minor/conf/log-allowlist.txt New allowlist for upgrade-minor log scanning.
e2e-tests/tests/upgrade-minor/13-check-logs.yaml Add post-upgrade log scan step.
e2e-tests/tests/upgrade-minor/12-collation-mismatch.sql Add SQL used to detect collation mismatch after upgrade.
e2e-tests/tests/upgrade-minor/12-check-collation-mismatch.yaml Point check to the new SQL file name/location.
e2e-tests/tests/upgrade-minor/11-read-from-primary.yaml Fix configmap naming to match step index.
e2e-tests/tests/upgrade-minor/11-assert.yaml Assert data read-back from primary after additional write.
e2e-tests/tests/upgrade-minor/10-write-data.yaml Add extra write step (post-upgrade) for read verification.
e2e-tests/tests/upgrade-minor/09-upgrade-cluster.yaml Strengthen upgrade step (timeouts, image waits, consistency checks, preconditions).
e2e-tests/tests/upgrade-minor/09-assert.yaml Update expected generations and status assertions for new flow.
e2e-tests/tests/upgrade-minor/08-restore.yaml Add restore step to validate cross-version backup/restore behavior.
e2e-tests/tests/upgrade-minor/08-assert.yaml Assert restore CR succeeded and cluster returned to ready.
e2e-tests/tests/upgrade-minor/07-backup-old-crversion.yaml Add backup step while cluster still has released crVersion.
e2e-tests/tests/upgrade-minor/07-assert.yaml Assert backup succeeded.
e2e-tests/tests/upgrade-minor/06-check-logs.yaml Add mid-upgrade log scan step.
e2e-tests/tests/upgrade-minor/05-sleep-after-operator-update.yaml Remove fixed sleep step (replaced by consistency/no-rollout checks).
e2e-tests/tests/upgrade-minor/05-check-no-rollout.yaml Add explicit “no operator-driven rollout” + heartbeat gap assertion.
e2e-tests/tests/upgrade-minor/05-assert.yaml Update expected generations/status due to new backup/flow behavior.
e2e-tests/tests/upgrade-minor/04-upgrade-operator.yaml Add baseline snapshot + heartbeat before operator update.
e2e-tests/tests/upgrade-minor/02-write-data.yaml Extend to include a full backup taken by released operator.
e2e-tests/tests/upgrade-minor/02-assert.yaml Assert pre-upgrade backup succeeded.
e2e-tests/tests/upgrade-minor/01-create-cluster.yaml Compose CR from released template + suite config; pin init image; repo2 wiring.
e2e-tests/tests/upgrade-minor/01-assert.yaml Increase timeout and assert repo2 stanza creation.
e2e-tests/tests/upgrade-minor/00-deploy-operator.yaml Add MinIO + repo2 credentials/bootstrap for cross-version backup/restore.
e2e-tests/tests/operator-upgrade-extensions/conf/operator-upgrade-extensions.yaml New suite config for extension survival across operator/cluster upgrade.
e2e-tests/tests/operator-upgrade-extensions/conf/log-allowlist.txt New allowlist for operator-upgrade-extensions log scanning.
e2e-tests/tests/operator-upgrade-extensions/99-remove-cluster-gracefully.yaml New cleanup step for suite teardown.
e2e-tests/tests/operator-upgrade-extensions/08-check-logs.yaml New log scan step after upgrade.
e2e-tests/tests/operator-upgrade-extensions/07-check-extensions-after-cluster-upgrade.yaml Validate extension set + pg_cron ticking + vector data after cluster upgrade.
e2e-tests/tests/operator-upgrade-extensions/06-upgrade-cluster.yaml Cluster upgrade step for the suite.
e2e-tests/tests/operator-upgrade-extensions/05-check-extensions-survived.yaml Validate no rollout and extension set unchanged after operator update.
e2e-tests/tests/operator-upgrade-extensions/04-upgrade-operator.yaml Operator upgrade step for the suite.
e2e-tests/tests/operator-upgrade-extensions/03-enable-extensions-and-write-data.yaml Install extensions + create data that must survive upgrades.
e2e-tests/tests/operator-upgrade-extensions/02-add-custom-pg-cron.yaml Add pg_cron as custom extension as part of migration scenario.
e2e-tests/tests/operator-upgrade-extensions/01-create-cluster.yaml Create suite cluster from released CR + suite config; canary for CRD pruning.
e2e-tests/tests/operator-upgrade-extensions/01-assert.yaml Assert suite cluster readiness.
e2e-tests/tests/operator-upgrade-extensions/00-deploy-operator.yaml Deploy released operator + MinIO/custom extensions bootstrap for suite.
e2e-tests/tests/operator-upgrade-extensions/00-assert.yaml Assert operator and MinIO are ready.
e2e-tests/tests/operator-upgrade-cert-manager/conf/log-allowlist.txt New allowlist for operator-upgrade-cert-manager log scanning.
e2e-tests/tests/operator-upgrade-cert-manager/99-remove-cluster-gracefully.yaml New cleanup step for suite teardown.
e2e-tests/tests/operator-upgrade-cert-manager/10-check-logs.yaml New log scan step after upgrade.
e2e-tests/tests/operator-upgrade-cert-manager/09-check-restored-data.yaml Verify restored data correctness.
e2e-tests/tests/operator-upgrade-cert-manager/08-restore.yaml Restore step to validate cross-version backup/restore.
e2e-tests/tests/operator-upgrade-cert-manager/08-assert.yaml Assert restore succeeded and cluster is ready post-restore.
e2e-tests/tests/operator-upgrade-cert-manager/07-backup-after-update.yaml Backup step after operator upgrade.
e2e-tests/tests/operator-upgrade-cert-manager/07-assert.yaml Assert post-upgrade backup succeeded.
e2e-tests/tests/operator-upgrade-cert-manager/06-check-tls-unchanged.yaml Validate cert-manager presence doesn’t take over PKI-issued secrets or roll pods.
e2e-tests/tests/operator-upgrade-cert-manager/05-upgrade-operator.yaml Operator upgrade step for cert-manager scenario.
e2e-tests/tests/operator-upgrade-cert-manager/04-backup.yaml Backup step before operator upgrade.
e2e-tests/tests/operator-upgrade-cert-manager/04-assert.yaml Assert pre-upgrade backup succeeded.
e2e-tests/tests/operator-upgrade-cert-manager/03-deploy-cert-manager.yaml Introduce cert-manager after PKI cluster exists (K8SPG-1017 trigger).
e2e-tests/tests/operator-upgrade-cert-manager/02-write-data-and-save-tls.yaml Write data + snapshot TLS/revisions baseline; premise checks.
e2e-tests/tests/operator-upgrade-cert-manager/01-create-cluster.yaml Create released cluster pinned to released init image; add repo2 config.
e2e-tests/tests/operator-upgrade-cert-manager/01-assert.yaml Assert cert-manager suite cluster readiness.
e2e-tests/tests/operator-upgrade-cert-manager/00-deploy-operator.yaml Deploy released operator + MinIO/TLS bootstrap for repo2 backups.
e2e-tests/tests/operator-upgrade-cert-manager/00-assert.yaml Assert operator and MinIO are ready.
e2e-tests/tests/custom-extensions/21-check-migration.yaml New migration verification: custom -> builtin must not drop pg_cron or its data.
e2e-tests/tests/custom-extensions/20-migrate-to-builtin.yaml Add migration step enabling builtin pg_cron while removing it from custom list.
e2e-tests/tests/custom-extensions/20-assert.yaml Assert expected status after migration step.
e2e-tests/tests/custom-extensions/19-enable-pg_cron.yaml Install pg_cron extension + create durable data (cron.job) for migration test.
e2e-tests/tests/custom-extensions/17-install-pg_cron.yaml Install pg_cron as custom extension to set up migration scenario.
e2e-tests/tests/custom-extensions/17-assert.yaml Assert expected status after custom pg_cron installation.
e2e-tests/run-release.csv Add new upgrade suites to release run list.
e2e-tests/run-pr.csv Add new upgrade suites to PR run list.
e2e-tests/functions Add shared helper functions for rollout/TLS/log assertions; adjust AWS endpoint.
Suppressed comments (1)

pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go:556

    1. Problem: new(*cr.Spec.Extensions.SetUser.Enabled) is invalid Go (the built-in new requires a type, not a value), so this code will not compile.
  1. Why it matters: This breaks PerconaPGCluster.ToCrunchy compilation and prevents reconciliation/builds.
  2. Fix: Allocate the bool pointer via ptr.To(*...) (or b := *...; postgresCluster.Spec.Extensions.SetUser = &b).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 547 to 551
if cr.Spec.Extensions.PGCron.Enabled != nil {
postgresCluster.Spec.Extensions.PGCron = *cr.Spec.Extensions.PGCron.Enabled
postgresCluster.Spec.Extensions.PGCron = new(*cr.Spec.Extensions.PGCron.Enabled)
} else {
postgresCluster.Spec.Extensions.PGCron = nil
}

@oksana-grishchenko oksana-grishchenko 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.

LGTM, few non-blocking suggestions

Comment thread e2e-tests/functions
Comment on lines 547 to +548
AWS_DEFAULT_REGION=eu-central-1 \
/usr/bin/aws --endpoint-url https://s3.amazonaws.com s3 cp s3://pg-extensions/ /tmp/ --recursive &&
/usr/bin/aws --endpoint-url https://s3.eu-central-1.amazonaws.com s3 cp s3://pg-extensions/ /tmp/ --recursive &&

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 suggest not to hardcode the region since we have the AWS_DEFAULT_REGION var above.
In fact, I think we should not specifying the endpoint --endpoint-url https://s3.eu-central-1.amazonaws.com at all, it should be resolved automatically since we've set the AWS_DEFAULT_REGION env

Comment on lines +286 to +287
// tri-state: nil means the user never asked for the extension, so the
// operator must leave it alone instead of dropping 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.

it would be more straightforward if the comment specified what leaving alone technically means. something like:

// tri-state: nil means the CR does not express a desired state for the extension, 
// so the operator must not create or drop the extension, 
// it preserves whatever state already exists in the database

wdyt?

@oksana-grishchenko

Copy link
Copy Markdown
Contributor

sorry for accidental closing the PR, it was a misclick somehow🙈

@egegunes egegunes added this to the v3.1.0 milestone Aug 13, 2026
@mayankshah1607

Copy link
Copy Markdown
Member

@nmarukovich please add a title

@egegunes egegunes 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.

I don't understand what's the result of these changes: if pg_cron was installed as a custom extension, what happens when you upgrade?

to me it seems like custom extension is not uninstalled even though you delete it from extensions.custom, and whatever version you have is kept. if this is true, this version is kept until when? is it possible that you think you're on pg_cron x.y.z while you're on x.y.z-6?

SetUser bool `json:"setUser,omitempty"`
// tri-state: nil means the user never asked for the extension, so the
// operator must leave it alone instead of dropping it
PGCron *bool `json:"pgCron,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.

should we allow to go from true -> nil?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants