diff --git a/internal/controller/postgrescluster/controller.go b/internal/controller/postgrescluster/controller.go index d459d30a10..2a622eb0ee 100644 --- a/internal/controller/postgrescluster/controller.go +++ b/internal/controller/postgrescluster/controller.go @@ -136,6 +136,16 @@ func (r *Reconciler) Reconcile( return runtime.ErrorWithBackoff(err) } } + // Issue Warning Event if postgres version is EOL according to PostgreSQL: + // https://www.postgresql.org/support/versioning/ + currentTime := time.Now() + if postgres.ReleaseIsFinal(cluster.Spec.PostgresVersion, currentTime) { + r.Recorder.Eventf(cluster, corev1.EventTypeWarning, "EndOfLifePostgresVersion", + "The last minor version of Postgres %[1]v has been released."+ + " PG %[1]v will no longer receive updates. We recommend upgrading."+ + " See https://www.postgresql.org/support/versioning", + cluster.Spec.PostgresVersion) + } if cluster.Spec.Standby != nil && cluster.Spec.Standby.Enabled && diff --git a/internal/controller/postgrescluster/controller_test.go b/internal/controller/postgrescluster/controller_test.go index e6fdc5cb86..d6f3730623 100644 --- a/internal/controller/postgrescluster/controller_test.go +++ b/internal/controller/postgrescluster/controller_test.go @@ -556,4 +556,66 @@ spec: Expect(instance.Spec.Replicas).To(PointTo(BeEquivalentTo(1))) }) }) + + Context("Postgres version EOL", func() { + var cluster *v1beta1.PostgresCluster + + BeforeEach(func() { + cluster = create(` +metadata: + name: old-postgres +spec: + postgresVersion: 11 + image: postgres + instances: + - name: instance1 + dataVolumeClaimSpec: + accessModes: + - "ReadWriteMany" + resources: + requests: + storage: 1Gi + backups: + pgbackrest: + image: pgbackrest + repos: + - name: repo1 + volume: + volumeClaimSpec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: 1Gi +`) + Expect(reconcile(cluster)).To(BeZero()) + }) + + AfterEach(func() { + ctx := context.Background() + + if cluster != nil { + Expect(client.IgnoreNotFound( + suite.Client.Delete(ctx, cluster), + )).To(Succeed()) + + // Remove finalizers, if any, so the namespace can terminate. + Expect(client.IgnoreNotFound( + suite.Client.Patch(ctx, cluster, client.RawPatch( + client.Merge.Type(), []byte(`{"metadata":{"finalizers":[]}}`))), + )).To(Succeed()) + } + }) + + Specify("Postgres EOL Warning Event", func() { + existing := &v1beta1.PostgresCluster{} + Expect(suite.Client.Get( + context.Background(), client.ObjectKeyFromObject(cluster), existing, + )).To(Succeed()) + + event, ok := <-test.Recorder.Events + Expect(ok).To(BeTrue()) + Expect(event).To(ContainSubstring("PG 11 will no longer receive updates. We recommend upgrading.")) + }) + }) }) diff --git a/internal/postgres/versions.go b/internal/postgres/versions.go new file mode 100644 index 0000000000..8a5e544040 --- /dev/null +++ b/internal/postgres/versions.go @@ -0,0 +1,26 @@ +// Copyright 2021 - 2024 Crunchy Data Solutions, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +package postgres + +import "time" + +// https://www.postgresql.org/support/versioning +var finalReleaseDates = map[int]time.Time{ + 10: time.Date(2022, time.November+1, 10, 0, 0, 0, 0, time.UTC), + 11: time.Date(2023, time.November+1, +9, 0, 0, 0, 0, time.UTC), + 12: time.Date(2024, time.November+1, 14, 0, 0, 0, 0, time.UTC), + 13: time.Date(2025, time.November+1, 13, 0, 0, 0, 0, time.UTC), + 14: time.Date(2026, time.November+1, 12, 0, 0, 0, 0, time.UTC), + 15: time.Date(2027, time.November+1, 11, 0, 0, 0, 0, time.UTC), + 16: time.Date(2028, time.November+1, +9, 0, 0, 0, 0, time.UTC), + 17: time.Date(2029, time.November+1, +8, 0, 0, 0, 0, time.UTC), +} + +// ReleaseIsFinal returns whether or not t is definitively past the final +// scheduled release of a Postgres version. +func ReleaseIsFinal(majorVersion int, t time.Time) bool { + known, ok := finalReleaseDates[majorVersion] + return ok && t.After(known) +} diff --git a/internal/postgres/versions_test.go b/internal/postgres/versions_test.go new file mode 100644 index 0000000000..7d2bd96c60 --- /dev/null +++ b/internal/postgres/versions_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 - 2024 Crunchy Data Solutions, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +package postgres + +import ( + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func TestReleaseIsFinal(t *testing.T) { + // On November 4th, 2024, PG 10 and 11 were EOL and 12-17 were supported. + testDate, err := time.Parse("2006-Jan-02", "2024-Nov-04") + assert.NilError(t, err) + assert.Check(t, ReleaseIsFinal(10, testDate)) + assert.Check(t, ReleaseIsFinal(11, testDate)) + assert.Check(t, !ReleaseIsFinal(12, testDate)) + assert.Check(t, !ReleaseIsFinal(13, testDate)) + assert.Check(t, !ReleaseIsFinal(14, testDate)) + assert.Check(t, !ReleaseIsFinal(15, testDate)) + assert.Check(t, !ReleaseIsFinal(16, testDate)) + assert.Check(t, !ReleaseIsFinal(17, testDate)) + + // On December 15th, 2024 we alert that PG 12 is EOL + testDate = testDate.AddDate(0, 1, 11) + assert.Check(t, ReleaseIsFinal(12, testDate)) + + // ReleaseIsFinal covers PG versions 10 and greater. Any version not covered + // by the case statement in ReleaseIsFinal returns false + assert.Check(t, !ReleaseIsFinal(1, testDate)) +}